use std::ffi::OsStr;

use actix_web::web::Bytes;

use crate::{
    formats::{AnimationFormat, ImageFormat},
    magick::{MagickError, MAGICK_TEMPORARY_PATH},
    process::{Process, ProcessRead},
    tmp_file::TmpDir,
};

pub(super) async fn convert_image(
    tmp_dir: &TmpDir,
    input: ImageFormat,
    output: ImageFormat,
    quality: Option<u8>,
    timeout: u64,
    bytes: Bytes,
) -> Result<ProcessRead, MagickError> {
    convert(
        tmp_dir,
        input.magick_format(),
        output.magick_format(),
        false,
        quality,
        timeout,
        bytes,
    )
    .await
}

pub(super) async fn convert_animation(
    tmp_dir: &TmpDir,
    input: AnimationFormat,
    output: AnimationFormat,
    quality: Option<u8>,
    timeout: u64,
    bytes: Bytes,
) -> Result<ProcessRead, MagickError> {
    convert(
        tmp_dir,
        input.magick_format(),
        output.magick_format(),
        true,
        quality,
        timeout,
        bytes,
    )
    .await
}

async fn convert(
    tmp_dir: &TmpDir,
    input: &'static str,
    output: &'static str,
    coalesce: bool,
    quality: Option<u8>,
    timeout: u64,
    bytes: Bytes,
) -> Result<ProcessRead, MagickError> {
    let temporary_path = tmp_dir
        .tmp_folder()
        .await
        .map_err(MagickError::CreateTemporaryDirectory)?;

    let input_file = tmp_dir.tmp_file(None);

    crate::store::file_store::safe_create_parent(&input_file)
        .await
        .map_err(MagickError::CreateDir)?;

    let mut tmp_one = crate::file::File::create(&input_file)
        .await
        .map_err(MagickError::CreateFile)?;
    tmp_one
        .write_from_bytes(bytes)
        .await
        .map_err(MagickError::Write)?;
    tmp_one.close().await.map_err(MagickError::CloseFile)?;

    let input_arg = [input.as_ref(), input_file.as_os_str()].join(":".as_ref());
    let output_arg = format!("{output}:-");
    let quality = quality.map(|q| q.to_string());

    let mut args: Vec<&OsStr> = vec!["convert".as_ref()];

    if coalesce {
        args.push("-coalesce".as_ref());
    }

    args.extend(["-strip".as_ref(), "-auto-orient".as_ref(), &input_arg] as [&OsStr; 3]);

    if let Some(quality) = &quality {
        args.extend(["-quality".as_ref(), quality.as_ref()] as [&OsStr; 2]);
    }

    args.push(output_arg.as_ref());

    let envs = [(MAGICK_TEMPORARY_PATH, temporary_path.as_os_str())];

    let reader = Process::run("magick", &args, &envs, timeout)?.read();

    let clean_reader = reader.add_extras(input_file).add_extras(temporary_path);

    Ok(clean_reader)
}
