//! Image upscaling implementation

use std::path::Path;
use std::time::Instant;
use crate::core::{TvaiError, TvaiProcessor, ProcessResult, ProgressCallback};
use crate::image::{ImageUpscaleParams, ImageFormat};

/// Image upscaling implementation
impl TvaiProcessor {
    /// Upscale a single image using Topaz AI models
    pub async fn upscale_image(
        &mut self,
        input_path: &Path,
        output_path: &Path,
        params: ImageUpscaleParams,
        progress_callback: Option<&ProgressCallback>,
    ) -> Result<ProcessResult, TvaiError> {
        let start_time = Instant::now();

        // Validate inputs
        self.validate_input_file(input_path)?;
        self.validate_output_path(output_path)?;

        // Validate parameters
        self.validate_image_upscale_params(&params)?;

        let operation_id = self.generate_operation_id();

        if let Some(callback) = progress_callback {
            callback(0.0);
        }

        // Build Topaz upscaling filter for images
        let upscale_filter = self.build_image_upscale_filter(&params)?;

        if let Some(callback) = progress_callback {
            callback(0.1);
        }

        // Build FFmpeg command for image processing
        let mut args = vec![
            "-y", "-hide_banner", "-nostdin",
            "-i", input_path.to_str().unwrap(),
            "-vf", &upscale_filter,
        ];

        // Add output format settings
        match params.output_format {
            ImageFormat::Png => {
                args.extend_from_slice(&["-f", "image2", "-compression_level", "6"]);
            }
            ImageFormat::Jpg => {
                args.extend_from_slice(&["-f", "image2", "-q:v", "2"]);
            }
            ImageFormat::Tiff => {
                args.extend_from_slice(&["-f", "image2", "-compression_algo", "lzw"]);
            }
            ImageFormat::Bmp => {
                args.extend_from_slice(&["-f", "image2"]);
            }
        }

        args.push(output_path.to_str().unwrap());

        if let Some(callback) = progress_callback {
            callback(0.2);
        }

        // Execute Topaz upscaling (requires Topaz FFmpeg)
        self.execute_ffmpeg_command(&args, true, progress_callback).await?;

        let processing_time = start_time.elapsed();

        // Get FFmpeg version for metadata
        let ffmpeg_version = self.get_ffmpeg_version(true).await.ok();

        // Create result metadata
        let mut metadata = self.create_metadata(
            operation_id,
            input_path,
            format!("image_upscale: model={}, scale={}, compression={}, blend={}, format={}",
                params.model.as_str(),
                params.scale_factor,
                params.compression,
                params.blend,
                params.output_format.extension()
            ),
        );
        metadata.ffmpeg_version = ffmpeg_version;

        if let Some(callback) = progress_callback {
            callback(1.0);
        }

        Ok(ProcessResult {
            output_path: output_path.to_path_buf(),
            processing_time,
            metadata,
        })
    }

    /// Validate image upscaling parameters
    fn validate_image_upscale_params(&self, params: &ImageUpscaleParams) -> Result<(), TvaiError> {
        if params.scale_factor < 1.0 || params.scale_factor > 4.0 {
            return Err(TvaiError::InvalidParameter(
                format!("Scale factor must be between 1.0 and 4.0, got {}", params.scale_factor)
            ));
        }

        if params.compression < -1.0 || params.compression > 1.0 {
            return Err(TvaiError::InvalidParameter(
                format!("Compression must be between -1.0 and 1.0, got {}", params.compression)
            ));
        }

        if params.blend < 0.0 || params.blend > 1.0 {
            return Err(TvaiError::InvalidParameter(
                format!("Blend must be between 0.0 and 1.0, got {}", params.blend)
            ));
        }

        // Check if model forces a specific scale
        if let Some(forced_scale) = params.model.forces_scale() {
            if (params.scale_factor - forced_scale).abs() > 0.01 {
                return Err(TvaiError::InvalidParameter(
                    format!("Model {} forces scale factor {}, but {} was requested",
                        params.model.as_str(), forced_scale, params.scale_factor)
                ));
            }
        }

        Ok(())
    }

    /// Build Topaz upscaling filter string for images
    fn build_image_upscale_filter(&self, params: &ImageUpscaleParams) -> Result<String, TvaiError> {
        let filter = format!(
            "tvai_up=model={}:scale={}:estimate=8:compression={}:blend={}",
            params.model.as_str(),
            params.scale_factor,
            params.compression,
            params.blend
        );

        Ok(filter)
    }

    /// Batch upscale multiple images
    pub async fn batch_upscale_images(
        &mut self,
        input_paths: &[std::path::PathBuf],
        output_dir: &Path,
        params: ImageUpscaleParams,
        progress_callback: Option<&ProgressCallback>,
    ) -> Result<Vec<ProcessResult>, TvaiError> {
        if input_paths.is_empty() {
            return Err(TvaiError::InvalidParameter("No input images provided".to_string()));
        }

        // Validate output directory
        std::fs::create_dir_all(output_dir)?;

        // Validate all input files
        for input_path in input_paths {
            self.validate_input_file(input_path)?;
        }

        let mut results = Vec::new();
        let total_images = input_paths.len();

        if let Some(callback) = progress_callback {
            callback(0.0);
        }

        for (index, input_path) in input_paths.iter().enumerate() {
            // Generate output filename
            let input_stem = input_path.file_stem()
                .and_then(|s| s.to_str())
                .unwrap_or("image");
            let output_filename = format!("{}_upscaled.{}", input_stem, params.output_format.extension());
            let output_path = output_dir.join(output_filename);

            // Process individual image (simplified progress for now)
            let result = self.upscale_image(
                input_path,
                &output_path,
                params.clone(),
                None, // TODO: Fix progress callback forwarding
            ).await?;

            // Update overall progress
            if let Some(callback) = progress_callback {
                let overall_progress = (index + 1) as f32 / total_images as f32;
                callback(overall_progress);
            }

            results.push(result);
        }

        if let Some(callback) = progress_callback {
            callback(1.0);
        }

        Ok(results)
    }

    /// Auto-detect and upscale images in a directory
    pub async fn upscale_directory(
        &mut self,
        input_dir: &Path,
        output_dir: &Path,
        params: ImageUpscaleParams,
        recursive: bool,
        progress_callback: Option<&ProgressCallback>,
    ) -> Result<Vec<ProcessResult>, TvaiError> {
        // Collect image files from directory
        let image_paths = self.collect_image_files(input_dir, recursive)?;

        if image_paths.is_empty() {
            return Err(TvaiError::InvalidParameter(
                format!("No image files found in directory: {}", input_dir.display())
            ));
        }

        // Process batch
        self.batch_upscale_images(&image_paths, output_dir, params, progress_callback).await
    }

    /// Collect image files from directory
    fn collect_image_files(&self, dir: &Path, recursive: bool) -> Result<Vec<std::path::PathBuf>, TvaiError> {
        let mut image_files = Vec::new();
        let supported_extensions = ["jpg", "jpeg", "png", "tiff", "tif", "bmp"];

        if recursive {
            self.collect_images_recursive(dir, &mut image_files, &supported_extensions)?;
        } else {
            if let Ok(entries) = std::fs::read_dir(dir) {
                for entry in entries.flatten() {
                    let path = entry.path();
                    if path.is_file() {
                        if let Some(extension) = path.extension() {
                            let ext_str = extension.to_string_lossy().to_lowercase();
                            if supported_extensions.contains(&ext_str.as_str()) {
                                image_files.push(path);
                            }
                        }
                    }
                }
            }
        }

        // Sort for consistent processing order
        image_files.sort();
        Ok(image_files)
    }

    /// Recursively collect image files
    fn collect_images_recursive(
        &self,
        dir: &Path,
        image_files: &mut Vec<std::path::PathBuf>,
        supported_extensions: &[&str],
    ) -> Result<(), TvaiError> {
        if let Ok(entries) = std::fs::read_dir(dir) {
            for entry in entries.flatten() {
                let path = entry.path();
                if path.is_dir() {
                    self.collect_images_recursive(&path, image_files, supported_extensions)?;
                } else if path.is_file() {
                    if let Some(extension) = path.extension() {
                        let ext_str = extension.to_string_lossy().to_lowercase();
                        if supported_extensions.contains(&ext_str.as_str()) {
                            image_files.push(path);
                        }
                    }
                }
            }
        }
        Ok(())
    }
}
