use std::fs;
use std::path::PathBuf;

#[derive(Clone)]
pub struct ImageCache {
    root: PathBuf,
}

impl ImageCache {
    pub fn new(root: PathBuf) -> Self {
        Self { root }
    }

    #[allow(dead_code)]
    pub fn game_dir(&self, source: &str, app_id_or_slug: &str) -> Result<PathBuf, String> {
        let safe_slug = app_id_or_slug
            .chars()
            .map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '-' })
            .collect::<String>();
        let dir = self.root.join(source).join(safe_slug);
        fs::create_dir_all(&dir).map_err(|err| format!("Failed to prepare image cache: {err}"))?;
        Ok(dir)
    }

    #[allow(dead_code)]
    pub fn local_image_path(
        &self,
        source: &str,
        app_id_or_slug: &str,
        image_kind: &str,
        extension: &str,
    ) -> Result<PathBuf, String> {
        Ok(self
            .game_dir(source, app_id_or_slug)?
            .join(format!("{image_kind}.{extension}")))
    }

    pub fn cache_remote_image(
        &self,
        source: &str,
        app_id_or_slug: &str,
        image_kind: &str,
        extension: &str,
        url: &str,
    ) -> Result<Option<PathBuf>, String> {
        let target = self.local_image_path(source, app_id_or_slug, image_kind, extension)?;
        if target.is_file() {
            if is_valid_image_file(&target) {
                return Ok(Some(target));
            }
            let _ = fs::remove_file(&target);
        }

        let response = match ureq::get(url)
            .header("User-Agent", "NebulaOS/0.1 library-scanner")
            .call()
        {
            Ok(response) => response,
            Err(_) => return Ok(None),
        };

        if !response.status().is_success() {
            return Ok(None);
        }

        let mut response = response;
        let bytes = response
            .body_mut()
            .read_to_vec()
            .map_err(|err| format!("Failed to download image {url}: {err}"))?;
        if !looks_like_image(&bytes) {
            return Ok(None);
        }
        fs::write(&target, bytes)
            .map_err(|err| format!("Failed to write image cache {}: {err}", target.display()))?;

        Ok(Some(target))
    }

    pub fn cache_first_remote_image(
        &self,
        source: &str,
        app_id_or_slug: &str,
        image_kind: &str,
        extension: &str,
        urls: &[String],
    ) -> Result<Option<PathBuf>, String> {
        for url in urls {
            if let Some(path) =
                self.cache_remote_image(source, app_id_or_slug, image_kind, extension, url)?
            {
                return Ok(Some(path));
            }
        }

        Ok(None)
    }
}

fn is_valid_image_file(path: &PathBuf) -> bool {
    fs::read(path)
        .map(|bytes| looks_like_image(&bytes))
        .unwrap_or(false)
}

fn looks_like_image(bytes: &[u8]) -> bool {
    if bytes.len() < 1024 {
        return false;
    }

    bytes.starts_with(&[0xff, 0xd8, 0xff])
        || bytes.starts_with(&[0x89, b'P', b'N', b'G'])
        || bytes.starts_with(b"RIFF") && bytes.get(8..12) == Some(b"WEBP")
}
