use std::{io::Cursor, path::Path};

use anyhow::{bail, Context};
use image::{io::Reader as ImageReader, DynamicImage, ImageOutputFormat};

use crate::{load::Loadable, resp::Response, util::slugify};

pub struct MyImage {
    original: DynamicImage,
    thumbnail: DynamicImage,
    format: ImageOutputFormat,
    original_url: String,
    thumbnail_url: String,
}

impl MyImage {
    // (original, thumbnail)
    pub fn into_responses(self) -> anyhow::Result<(String, Response, String, Response)> {
        let content_type = format_to_content_type(&self.format)?;

        let mut original = vec![];
        self.original
            .write_to(&mut Cursor::new(&mut original), self.format.clone())?;

        let mut thumbnail = vec![];
        self.thumbnail
            .write_to(&mut Cursor::new(&mut thumbnail), self.format)?;

        Ok((
            self.original_url,
            Response {
                content_type,
                data: original,
            },
            self.thumbnail_url,
            Response {
                content_type,
                data: thumbnail,
            },
        ))
    }
}

impl Loadable for MyImage {
    fn load(path: &Path, slug_prefix: &str) -> anyhow::Result<MyImage> {
        let file_name = path
            .file_name()
            .context("Could not get file name")?
            .to_str()
            .context("Could not convert filename into string")?
            .to_owned();

        let original_url = slugify(&format!("/img/{slug_prefix}/{file_name}"));
        let thumbnail_url = slugify(&format!("/thumb/{slug_prefix}/{file_name}"));

        let reader = ImageReader::open(path)?;
        let format = reader
            .format()
            .context("Could not determine image format")?
            .into();
        let original = reader.decode()?;
        let thumbnail = original.thumbnail(400, 800);

        Ok(Self {
            original,
            thumbnail,
            format,
            original_url,
            thumbnail_url,
        })
    }
}

fn format_to_content_type(f: &ImageOutputFormat) -> anyhow::Result<&'static str> {
    let s = match f {
        ImageOutputFormat::Png => "image/png",
        ImageOutputFormat::Jpeg(_) => "image/jpg",
        ImageOutputFormat::Bmp => "image/bmp",
        ImageOutputFormat::Gif => "image/gif",
        ImageOutputFormat::Ico => "image/x-icon",

        _ => bail!("Invalid content type {:?}", f),
    };

    Ok(s)
}