use gpui::{AssetSource, Result, SharedString};
use std::borrow::Cow;

// `icon_bytes()`/`ICON_ASSETS` and `file_icon_bytes()`/`FILE_ICON_ASSETS`,
// generated by `build.rs` from `assets/icons/*.svg` and
// `assets/icons/file_icons/*.svg`.
include!(concat!(env!("OUT_DIR"), "/icons_assets.rs"));

pub struct GitCometAssets;

impl GitCometAssets {
    fn load_static(path: &str) -> Option<Cow<'static, [u8]>> {
        match path {
            "gitcomet-window-icon.png" => Some(Cow::Borrowed(include_bytes!(
                "../../../assets/gitcomet-window-icon.png"
            ))),
            "gitcomet-512.png" => Some(Cow::Borrowed(include_bytes!(
                "../../../assets/gitcomet-512.png"
            ))),
            "gitcomet_logo.svg" => Some(Cow::Borrowed(include_bytes!(
                "../../../assets/gitcomet_logo.svg"
            ))),
            other => icon_bytes(other)
                .or_else(|| file_icon_bytes(other))
                .map(Cow::Borrowed),
        }
    }

    fn list_static(dir: &str) -> Vec<SharedString> {
        match dir.trim_end_matches('/') {
            "" => vec![
                "gitcomet-window-icon.png".into(),
                "gitcomet-512.png".into(),
                "gitcomet_logo.svg".into(),
                "icons".into(),
            ],
            "icons" => ICON_ASSETS
                .iter()
                .map(|path| SharedString::from(*path))
                .chain(std::iter::once("icons/file_icons".into()))
                .collect(),
            "icons/file_icons" => FILE_ICON_ASSETS
                .iter()
                .map(|path| SharedString::from(*path))
                .collect(),
            _ => vec![],
        }
    }
}

impl AssetSource for GitCometAssets {
    fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
        Ok(Self::load_static(path))
    }

    fn list(&self, path: &str) -> Result<Vec<SharedString>> {
        Ok(Self::list_static(path))
    }
}

#[cfg(test)]
mod tests {
    use super::GitCometAssets;
    use std::{collections::BTreeSet, path::Path};

    #[test]
    fn icon_assets_directory_matches_registry() {
        let icons_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/icons");
        let on_disk: BTreeSet<String> = std::fs::read_dir(&icons_dir)
            .expect("expected icons asset directory")
            .map(|entry| {
                let entry = entry.expect("expected icon asset entry");
                let name = entry.file_name();
                format!("icons/{}", name.to_string_lossy())
            })
            .collect();

        let registered: BTreeSet<String> = GitCometAssets::list_static("icons")
            .into_iter()
            .map(|path| path.to_string())
            .collect();

        assert_eq!(
            registered, on_disk,
            "expected the asset registry to cover every icons/* entry"
        );

        for icon in &registered {
            if icon == "icons/file_icons" {
                continue;
            }
            assert!(
                GitCometAssets::load_static(icon).is_some(),
                "expected {icon} to be loadable from the asset registry"
            );
        }

        let file_icons_dir = icons_dir.join("file_icons");
        let file_icons_on_disk: BTreeSet<String> = std::fs::read_dir(&file_icons_dir)
            .expect("expected file_icons asset directory")
            .map(|entry| {
                let name = entry.expect("expected file icon entry").file_name();
                format!("icons/file_icons/{}", name.to_string_lossy())
            })
            .collect();
        let file_icons_registered: BTreeSet<String> =
            GitCometAssets::list_static("icons/file_icons")
                .into_iter()
                .map(|path| path.to_string())
                .collect();

        assert_eq!(
            file_icons_registered, file_icons_on_disk,
            "expected the registry to cover every icons/file_icons/*.svg file"
        );

        for icon in &file_icons_registered {
            assert!(
                GitCometAssets::load_static(icon).is_some(),
                "expected {icon} to be loadable from the asset registry"
            );
        }
    }
}
