use crate::fetch::print_fetch_with_image;
use crate::rotate::{apply_static, run_spin_fetch_loop, run_spin_loop, RotateMode};
use anyhow::Result;
use px2ansi::{RasterTheme, RenderOptions};
use std::io::Write;
use std::path::PathBuf;

/// Parameters for converting a single image file to ANSI art
#[derive(Debug)]
pub struct ConvertCmd {
    /// Path to the source image.
    pub input: PathBuf,
    /// Optional path to save the ANSI text output. If None, prints to stdout.
    pub output: Option<PathBuf>,
    /// Optional path to save a PNG rasterization of the result.
    pub output_image: Option<PathBuf>,
    /// Visual settings (width, filter, style).
    pub render: RenderOptions,
    pub raster_theme: RasterTheme,
    /// Optional rotation mode derived from `--rotate` / `--fps`.
    pub rotate: Option<RotateMode>,
    /// Optional system info fetch
    pub fetch: bool,
}

impl ConvertCmd {
    /// Runs the command.
    ///
    /// # Errors
    ///
    /// Returns an error if writing to the provided writer fails or if
    /// the command logic encounters a processing error.
    pub fn run<W: Write>(&self, external_writer: &mut W) -> Result<()> {
        // 1. Load and decode
        let img = image::ImageReader::open(&self.input)?.decode()?;

        // 2. Handle spin mode early: it never returns, so we branch out here.

        match (&self.rotate, self.fetch) {
            (
                Some(RotateMode::Spin {
                    fps,
                    axis,
                    unidirectional,
                }),
                true,
            ) => {
                return run_spin_fetch_loop(
                    &img,
                    &self.render,
                    *fps,
                    *axis,
                    *unidirectional,
                    external_writer,
                );
            }
            (
                Some(RotateMode::Spin {
                    fps,
                    axis,
                    unidirectional,
                }),
                false,
            ) => {
                return run_spin_loop(
                    &img,
                    &self.render,
                    *fps,
                    *axis,
                    *unidirectional,
                    external_writer,
                );
            }
            _ => {}
        }

        // Static rotation
        let img = match &self.rotate {
            Some(RotateMode::Static(deg)) => apply_static(img, *deg),
            _ => img,
        };

        // Fetch layout (static or no rotation)
        if self.fetch {
            print_fetch_with_image(&img, &self.render, external_writer)?;
            external_writer.flush()?;
            return Ok(());
        } // if let Some(RotateMode::Spin {
          //     fps,
          //     axis,
          //     unidirectional,
          // }) = self.rotate
          // {
          //     return run_spin_loop(
          //         &img,
          //         &self.render,
          //         fps,
          //         axis,
          //         unidirectional,
          //         external_writer,
          //     );
          // }

        // 3. Apply static rotation if requested.
        let img = match self.rotate {
            Some(RotateMode::Static(deg)) => apply_static(img, deg),
            _ => img,
        };

        // 3. Setup the file writer if needed
        let mut file_writer = self
            .output
            .as_ref()
            .map(|path| std::fs::File::create(path).map(std::io::BufWriter::new))
            .transpose()?;

        // 4. Render and Rasterize logic
        #[cfg(feature = "rasterize")]
        if let Some(png_path) = self.output_image.as_ref() {
            // Buffer is required for PNG rasterization
            let mut buf = Vec::with_capacity(img.width() as usize * img.height() as usize * 2);
            self.render.render_centered(&img, &mut buf)?;

            // Resolve where to write the buffer (File or External)
            let target: &mut dyn Write = match file_writer.as_mut() {
                Some(fw) => fw,
                None => external_writer,
            };

            target.write_all(&buf)?;
            target.flush()?;

            // Handle optional PNG rasterization
            let rasterized = px2ansi::rasterize_ansi_with_theme(&buf, self.raster_theme)?;
            rasterized.save(png_path)?;

            // Log to terminal
            writeln!(
                external_writer,
                "✅ Saved preview to {}",
                png_path.display()
            )?;
            return Ok(());
        }

        // Handle case where user wants a PNG but didn't compile with the feature
        #[cfg(not(feature = "rasterize"))]
        if self.output_image.is_some() {
            anyhow::bail!(
                "PNG rasterization requires the 'rasterize' feature. Rebuild with: cargo build --features rasterize"
            );
        }

        // FAST PATH: No preview image needed, stream directly
        let mut target: &mut dyn Write = match file_writer.as_mut() {
            Some(fw) => fw,
            None => external_writer,
        };

        self.render.render_centered(&img, &mut target)?;
        target.flush()?;

        Ok(())
    }
}
