use std::fs;
use std::path::{Path, PathBuf};
use crate::types::Result;
use crate::utils::log::{success};
use image::{DynamicImage, ImageFormat};
use super::commands::{ConvertArgs, ImgFormat};

fn to_image_format(fmt: &ImgFormat) -> ImageFormat {
    match fmt {
        ImgFormat::Png => ImageFormat::Png,
        ImgFormat::Jpeg => ImageFormat::Jpeg,
        ImgFormat::Webp => ImageFormat::WebP,
        ImgFormat::Gif => ImageFormat::Gif,
        ImgFormat::Tiff => ImageFormat::Tiff,
        ImgFormat::Avif => ImageFormat::Avif,
        ImgFormat::Bmp => ImageFormat::Bmp,
        ImgFormat::Dds => ImageFormat::Dds,
        ImgFormat::Exr => ImageFormat::OpenExr,
        ImgFormat::Ff => ImageFormat::Farbfeld,
        ImgFormat::Hdr => ImageFormat::Hdr,
        ImgFormat::Ico => ImageFormat::Ico,
        ImgFormat::Pnm => ImageFormat::Pnm,
        ImgFormat::Qoi => ImageFormat::Qoi,
        ImgFormat::Tga => ImageFormat::Tga,
    }
}

fn infer_out_dir(input: &Path, out_dir: Option<PathBuf>) -> PathBuf {
    match out_dir {
        Some(d) => d,
        None => input.parent().unwrap_or_else(|| Path::new(".")).to_path_buf(),
    }
}

fn build_out_path(input: &Path, out_dir: Option<PathBuf>, out_name: Option<String>, fmt: &ImgFormat) -> PathBuf {
    let dir = infer_out_dir(input, out_dir);
    let stem = out_name.unwrap_or_else(|| input.file_stem().unwrap_or_default().to_string_lossy().to_string());
    let ext = match fmt {
        ImgFormat::Png => "png",
        ImgFormat::Jpeg => "jpg",
        ImgFormat::Webp => "webp",
        ImgFormat::Gif => "gif",
        ImgFormat::Tiff => "tiff",
        ImgFormat::Avif => "avif",
        ImgFormat::Bmp => "bmp",
        ImgFormat::Dds => "dds",
        ImgFormat::Exr => "exr",
        ImgFormat::Ff => "ff",
        ImgFormat::Hdr => "hdr",
        ImgFormat::Ico => "ico",
        ImgFormat::Pnm => "pnm",
        ImgFormat::Qoi => "qoi",
        ImgFormat::Tga => "tga",
    };
    dir.join(format!("{}.{}", stem, ext))
}

pub fn execute(args: ConvertArgs) -> Result<()> {
    let input = args.input;
    if !input.exists() {
        return Err(format!("input not found: {}", input.display()).into());
    }

    let img = image::open(&input)?;
    let out = build_out_path(&input, args.out_dir, args.out_name, &args.format);
    if out.exists() {
        return Err(format!("output already exists: {}", out.display()).into());
    }
    if let Some(parent) = out.parent() { fs::create_dir_all(parent)?; }

    save_image(&img, &out, &args.format)?;
    success(&format!("saved: {}", out.display()));
    Ok(())
}

fn save_image(img: &DynamicImage, out: &Path, fmt: &ImgFormat) -> Result<()> {
    let format = to_image_format(fmt);
    img.save_with_format(out, format)?;
    Ok(())
}
