use image::{DynamicImage, ImageFormat};
use std::io::Cursor;
use tranquil_pds::image::{
    DEFAULT_MAX_FILE_SIZE, ImageError, ImageProcessor, OutputFormat, THUMB_SIZE_FEED,
    THUMB_SIZE_FULL,
};

fn create_test_png(width: u32, height: u32) -> Vec<u8> {
    let img = DynamicImage::new_rgb8(width, height);
    let mut buf = Vec::new();
    img.write_to(&mut Cursor::new(&mut buf), ImageFormat::Png)
        .unwrap();
    buf
}

fn create_test_jpeg(width: u32, height: u32) -> Vec<u8> {
    let img = DynamicImage::new_rgb8(width, height);
    let mut buf = Vec::new();
    img.write_to(&mut Cursor::new(&mut buf), ImageFormat::Jpeg)
        .unwrap();
    buf
}

fn create_test_gif(width: u32, height: u32) -> Vec<u8> {
    let img = DynamicImage::new_rgb8(width, height);
    let mut buf = Vec::new();
    img.write_to(&mut Cursor::new(&mut buf), ImageFormat::Gif)
        .unwrap();
    buf
}

fn create_test_webp(width: u32, height: u32) -> Vec<u8> {
    let img = DynamicImage::new_rgb8(width, height);
    let mut buf = Vec::new();
    img.write_to(&mut Cursor::new(&mut buf), ImageFormat::WebP)
        .unwrap();
    buf
}

#[test]
fn test_format_support() {
    let processor = ImageProcessor::new();

    let png = create_test_png(500, 500);
    let result = processor.process(&png, "image/png").unwrap();
    assert_eq!(result.original.width, 500);
    assert_eq!(result.original.height, 500);

    let jpeg = create_test_jpeg(400, 300);
    let result = processor.process(&jpeg, "image/jpeg").unwrap();
    assert_eq!(result.original.width, 400);
    assert_eq!(result.original.height, 300);

    let gif = create_test_gif(200, 200);
    let result = processor.process(&gif, "image/gif").unwrap();
    assert_eq!(result.original.width, 200);

    let webp = create_test_webp(300, 200);
    let result = processor.process(&webp, "image/webp").unwrap();
    assert_eq!(result.original.width, 300);
}

#[test]
fn test_thumbnail_generation() {
    let processor = ImageProcessor::new();

    let small = create_test_png(100, 100);
    let result = processor.process(&small, "image/png").unwrap();
    assert!(
        result.thumbnail_feed.is_none(),
        "Small image should not get feed thumbnail"
    );
    assert!(
        result.thumbnail_full.is_none(),
        "Small image should not get full thumbnail"
    );

    let medium = create_test_png(500, 500);
    let result = processor.process(&medium, "image/png").unwrap();
    assert!(
        result.thumbnail_feed.is_some(),
        "Medium image should have feed thumbnail"
    );
    assert!(
        result.thumbnail_full.is_none(),
        "Medium image should NOT have full thumbnail"
    );

    let large = create_test_png(2000, 2000);
    let result = processor.process(&large, "image/png").unwrap();
    assert!(
        result.thumbnail_feed.is_some(),
        "Large image should have feed thumbnail"
    );
    assert!(
        result.thumbnail_full.is_some(),
        "Large image should have full thumbnail"
    );
    let thumb = result.thumbnail_feed.unwrap();
    assert!(thumb.width <= THUMB_SIZE_FEED && thumb.height <= THUMB_SIZE_FEED);
    let full = result.thumbnail_full.unwrap();
    assert!(full.width <= THUMB_SIZE_FULL && full.height <= THUMB_SIZE_FULL);

    let at_feed = create_test_png(THUMB_SIZE_FEED, THUMB_SIZE_FEED);
    let above_feed = create_test_png(THUMB_SIZE_FEED + 1, THUMB_SIZE_FEED + 1);
    assert!(
        processor
            .process(&at_feed, "image/png")
            .unwrap()
            .thumbnail_feed
            .is_none()
    );
    assert!(
        processor
            .process(&above_feed, "image/png")
            .unwrap()
            .thumbnail_feed
            .is_some()
    );

    let at_full = create_test_png(THUMB_SIZE_FULL, THUMB_SIZE_FULL);
    let above_full = create_test_png(THUMB_SIZE_FULL + 1, THUMB_SIZE_FULL + 1);
    assert!(
        processor
            .process(&at_full, "image/png")
            .unwrap()
            .thumbnail_full
            .is_none()
    );
    assert!(
        processor
            .process(&above_full, "image/png")
            .unwrap()
            .thumbnail_full
            .is_some()
    );

    let disabled = ImageProcessor::new().with_thumbnails(false);
    let result = disabled.process(&large, "image/png").unwrap();
    assert!(result.thumbnail_feed.is_none() && result.thumbnail_full.is_none());
}

#[test]
fn test_output_format_conversion() {
    let png = create_test_png(300, 300);
    let jpeg = create_test_jpeg(300, 300);

    let webp_proc = ImageProcessor::new().with_output_format(OutputFormat::WebP);
    assert_eq!(
        webp_proc
            .process(&png, "image/png")
            .unwrap()
            .original
            .mime_type,
        "image/webp"
    );

    let jpeg_proc = ImageProcessor::new().with_output_format(OutputFormat::Jpeg);
    assert_eq!(
        jpeg_proc
            .process(&png, "image/png")
            .unwrap()
            .original
            .mime_type,
        "image/jpeg"
    );

    let png_proc = ImageProcessor::new().with_output_format(OutputFormat::Png);
    assert_eq!(
        png_proc
            .process(&jpeg, "image/jpeg")
            .unwrap()
            .original
            .mime_type,
        "image/png"
    );
}

#[test]
fn test_size_and_dimension_limits() {
    assert_eq!(DEFAULT_MAX_FILE_SIZE, 10 * 1024 * 1024);

    let max_dim = ImageProcessor::new().with_max_dimension(1000);
    let large = create_test_png(2000, 2000);
    let result = max_dim.process(&large, "image/png");
    assert!(matches!(
        result,
        Err(ImageError::TooLarge {
            width: 2000,
            height: 2000,
            max_dimension: 1000
        })
    ));

    let max_file = ImageProcessor::new().with_max_file_size(100);
    let data = create_test_png(500, 500);
    let result = max_file.process(&data, "image/png");
    assert!(matches!(
        result,
        Err(ImageError::FileTooLarge { max_size: 100, .. })
    ));
}

#[test]
fn test_error_handling() {
    let processor = ImageProcessor::new();

    let result = processor.process(b"this is not an image", "application/octet-stream");
    assert!(matches!(result, Err(ImageError::UnsupportedFormat(_))));

    let result = processor.process(b"\x89PNG\r\n\x1a\ncorrupted data here", "image/png");
    assert!(matches!(result, Err(ImageError::DecodeError(_))));
}

#[test]
fn test_aspect_ratio_preservation() {
    let processor = ImageProcessor::new();

    let landscape = create_test_png(1600, 800);
    let result = processor.process(&landscape, "image/png").unwrap();
    let thumb = result.thumbnail_full.unwrap();
    let original_ratio = 1600.0 / 800.0;
    let thumb_ratio = thumb.width as f64 / thumb.height as f64;
    assert!((original_ratio - thumb_ratio).abs() < 0.1);

    let portrait = create_test_png(800, 1600);
    let result = processor.process(&portrait, "image/png").unwrap();
    let thumb = result.thumbnail_full.unwrap();
    let original_ratio = 800.0 / 1600.0;
    let thumb_ratio = thumb.width as f64 / thumb.height as f64;
    assert!((original_ratio - thumb_ratio).abs() < 0.1);
}

#[test]
fn test_utilities_and_builder() {
    assert!(ImageProcessor::is_supported_mime_type("image/jpeg"));
    assert!(ImageProcessor::is_supported_mime_type("image/jpg"));
    assert!(ImageProcessor::is_supported_mime_type("image/png"));
    assert!(ImageProcessor::is_supported_mime_type("image/gif"));
    assert!(ImageProcessor::is_supported_mime_type("image/webp"));
    assert!(ImageProcessor::is_supported_mime_type("IMAGE/PNG"));
    assert!(ImageProcessor::is_supported_mime_type("Image/Jpeg"));
    assert!(!ImageProcessor::is_supported_mime_type("image/bmp"));
    assert!(!ImageProcessor::is_supported_mime_type("image/tiff"));
    assert!(!ImageProcessor::is_supported_mime_type("text/plain"));

    let data = create_test_png(100, 100);
    let processor = ImageProcessor::new();
    let result = processor.process(&data, "application/octet-stream");
    assert!(result.is_ok(), "Should detect PNG format from data");

    let jpeg = create_test_jpeg(100, 100);
    let stripped = ImageProcessor::strip_exif(&jpeg).unwrap();
    assert!(!stripped.is_empty());

    let processor = ImageProcessor::new()
        .with_max_dimension(2048)
        .with_max_file_size(5 * 1024 * 1024)
        .with_output_format(OutputFormat::Jpeg)
        .with_thumbnails(true);
    let data = create_test_png(500, 500);
    let result = processor.process(&data, "image/png").unwrap();
    assert_eq!(result.original.mime_type, "image/jpeg");
    assert!(!result.original.data.is_empty());
    assert!(result.original.width > 0 && result.original.height > 0);
}
