use pixstrip_core::executor::PipelineExecutor;
use pixstrip_core::operations::*;
use pixstrip_core::pipeline::ProcessingJob;
use pixstrip_core::types::*;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};

fn create_test_jpeg(path: &std::path::Path) {
    let img = image::RgbImage::from_fn(200, 150, |x, y| {
        image::Rgb([(x % 256) as u8, (y % 256) as u8, 128])
    });
    img.save_with_format(path, image::ImageFormat::Jpeg).unwrap();
}

fn setup_test_dir() -> (tempfile::TempDir, std::path::PathBuf, std::path::PathBuf) {
    let dir = tempfile::tempdir().unwrap();
    let input = dir.path().join("input");
    let output = dir.path().join("output");
    std::fs::create_dir_all(&input).unwrap();
    std::fs::create_dir_all(&output).unwrap();
    (dir, input, output)
}

#[test]
fn execute_single_image_resize() {
    let (_dir, input, output) = setup_test_dir();
    create_test_jpeg(&input.join("photo.jpg"));

    let mut job = ProcessingJob::new(&input, &output);
    job.add_source(input.join("photo.jpg"));
    job.resize = Some(ResizeConfig::ByWidth(100));

    let executor = PipelineExecutor::new();
    let result = executor.execute(&job, |_| {}).unwrap();

    assert_eq!(result.succeeded, 1);
    assert_eq!(result.failed, 0);
    assert!(output.join("photo.jpg").exists());
}

#[test]
fn execute_resize_and_convert() {
    let (_dir, input, output) = setup_test_dir();
    create_test_jpeg(&input.join("photo.jpg"));

    let mut job = ProcessingJob::new(&input, &output);
    job.add_source(input.join("photo.jpg"));
    job.resize = Some(ResizeConfig::ByWidth(100));
    job.convert = Some(ConvertConfig::SingleFormat(ImageFormat::WebP));

    let executor = PipelineExecutor::new();
    let result = executor.execute(&job, |_| {}).unwrap();

    assert_eq!(result.succeeded, 1);
    assert!(output.join("photo.webp").exists());
}

#[test]
fn execute_multiple_images() {
    let (_dir, input, output) = setup_test_dir();
    create_test_jpeg(&input.join("a.jpg"));
    create_test_jpeg(&input.join("b.jpg"));
    create_test_jpeg(&input.join("c.jpg"));

    let mut job = ProcessingJob::new(&input, &output);
    job.add_source(input.join("a.jpg"));
    job.add_source(input.join("b.jpg"));
    job.add_source(input.join("c.jpg"));
    job.resize = Some(ResizeConfig::ByWidth(50));

    let executor = PipelineExecutor::new();
    let result = executor.execute(&job, |_| {}).unwrap();

    assert_eq!(result.succeeded, 3);
    assert_eq!(result.failed, 0);
    assert_eq!(result.total, 3);
}

#[test]
fn execute_collects_progress() {
    let (_dir, input, output) = setup_test_dir();
    create_test_jpeg(&input.join("photo.jpg"));

    let mut job = ProcessingJob::new(&input, &output);
    job.add_source(input.join("photo.jpg"));
    job.resize = Some(ResizeConfig::ByWidth(100));

    let updates = Arc::new(Mutex::new(Vec::new()));
    let updates_clone = updates.clone();

    let executor = PipelineExecutor::new();
    executor.execute(&job, move |update| {
        updates_clone.lock().unwrap().push(update);
    }).unwrap();

    let updates = updates.lock().unwrap();
    assert!(!updates.is_empty());
}

#[test]
fn execute_with_cancellation() {
    let (_dir, input, output) = setup_test_dir();
    create_test_jpeg(&input.join("a.jpg"));
    create_test_jpeg(&input.join("b.jpg"));

    let mut job = ProcessingJob::new(&input, &output);
    job.add_source(input.join("a.jpg"));
    job.add_source(input.join("b.jpg"));
    job.resize = Some(ResizeConfig::ByWidth(100));

    let cancel = Arc::new(AtomicBool::new(true)); // cancel immediately
    let executor = PipelineExecutor::with_cancel(cancel);
    let result = executor.execute(&job, |_| {}).unwrap();

    // Cancellation flag should be set
    assert!(result.cancelled, "result.cancelled should be true when cancel flag is set");
    // Total processed should be less than total sources (at least some skipped)
    assert!(
        result.succeeded + result.failed <= 2,
        "processed count ({}) should not exceed total (2)",
        result.succeeded + result.failed
    );
}

#[test]
fn batch_result_tracks_sizes() {
    let (_dir, input, output) = setup_test_dir();
    create_test_jpeg(&input.join("photo.jpg"));

    let mut job = ProcessingJob::new(&input, &output);
    job.add_source(input.join("photo.jpg"));
    job.compress = Some(CompressConfig::Preset(QualityPreset::Low));

    let executor = PipelineExecutor::new();
    let result = executor.execute(&job, |_| {}).unwrap();

    assert!(result.total_input_bytes > 0);
    assert!(result.total_output_bytes > 0);
}
