use super::{FilterOption, FilterPlugin};
use crate::common::PIPESIZE;
use crate::services::filter_service::register_filter_plugin;
use std::io::{BufRead, Read, Result, Write};

/// A filter that reads the first N bytes from the input stream.
///
/// Limits the output to the initial bytes specified in the configuration.
/// Useful for previewing file contents without reading everything.
///
/// # Fields
///
/// * `remaining` - Number of bytes left to read before stopping.
pub struct HeadBytesFilter {
    remaining: usize,
}

/// A filter that reads the first N bytes from the input stream.
///
/// Limits the output to the initial bytes specified in the configuration.
/// Useful for previewing file contents without reading everything.
///
/// # Fields
///
/// * `remaining` - Number of bytes left to read before stopping.
impl HeadBytesFilter {
    /// Creates a new `HeadBytesFilter` that will read up to the specified number of bytes.
    ///
    /// # Arguments
    ///
    /// * `count` - The maximum number of bytes to read from the input.
    ///
    /// # Returns
    ///
    /// A new instance configured to read at most `count` bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// # use keep::filter_plugin::HeadBytesFilter;
    /// let filter = HeadBytesFilter::new(1024);
    /// ```
    pub fn new(count: usize) -> Self {
        Self { remaining: count }
    }
}

/// Filters input by reading only the first N bytes and writing them to the output.
///
/// Reads from the input in chunks until the byte limit is reached or EOF, then writes
/// the collected bytes to the output. Stops early if the limit is zero.
///
/// # Arguments
///
/// * `reader` - Mutable reference to the input data stream.
/// * `writer` - Mutable reference to the output stream.
///
/// # Returns
///
/// * `Result<()>` - Success if filtering completes, or I/O error.
///
/// # Errors
///
/// * `io::Error` from reading or writing operations.
///
/// # Examples
///
/// ```
/// # use std::io::{Read, Write, Cursor};
/// # use keep::filter_plugin::{FilterPlugin, HeadBytesFilter};
/// # let mut filter = HeadBytesFilter::new(5);
/// let mut input: &mut dyn Read = &mut Cursor::new(b"Hello World");
/// let mut output = Vec::new();
/// filter.filter(&mut input, &mut output)?;
/// assert_eq!(output, b"Hello");
/// # Ok::<(), std::io::Error>(())
/// ```
impl FilterPlugin for HeadBytesFilter {
    fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
        if self.remaining == 0 {
            return Ok(());
        }

        let mut buffer = vec![0; PIPESIZE];
        while self.remaining > 0 {
            let to_read = std::cmp::min(self.remaining, PIPESIZE);
            let bytes_read = reader.read(&mut buffer[..to_read])?;
            if bytes_read == 0 {
                break;
            }
            writer.write_all(&buffer[..bytes_read])?;
            self.remaining -= bytes_read;
        }
        Ok(())
    }

    /// Clones this filter into a new boxed instance.
    ///
    /// Creates an independent copy with the same configuration.
    ///
    /// # Returns
    ///
    /// A new `Box<dyn FilterPlugin>` clone.
    ///
    /// # Examples
    ///
    /// ```
    /// # use keep::filter_plugin::{FilterPlugin, HeadBytesFilter};
    /// let filter = HeadBytesFilter::new(100);
    /// let cloned = filter.clone_box();
    /// ```
    fn clone_box(&self) -> Box<dyn FilterPlugin> {
        Box::new(Self {
            remaining: self.remaining,
        })
    }

    /// Returns the configuration options for this filter.
    ///
    /// Defines the "count" parameter as required with no default.
    ///
    /// # Returns
    ///
    /// Vector of `FilterOption` describing parameters.
    ///
    /// # Examples
    ///
    /// ```
    /// # use keep::filter_plugin::{FilterPlugin, HeadBytesFilter};
    /// let filter = HeadBytesFilter::new(100);
    /// let opts = filter.options();
    /// assert_eq!(opts.len(), 1);
    /// assert_eq!(opts[0].name, "count");
    /// assert!(opts[0].required);
    /// ```
    fn options(&self) -> Vec<FilterOption> {
        vec![FilterOption {
            name: "count".to_string(),
            default: None,
            required: true,
        }]
    }

    fn description(&self) -> &str {
        "Read the first N bytes"
    }
}

/// A filter that reads the first N lines from the input stream.
pub struct HeadLinesFilter {
    remaining: usize,
}

/// A filter that reads the first N lines from the input stream.
///
/// Limits output to the initial lines specified, writing each full line to output.
/// Handles line endings properly using buffered reading.
///
/// # Fields
///
/// * `remaining` - Number of lines left to read before stopping.
impl HeadLinesFilter {
    /// Creates a new `HeadLinesFilter` that will read up to the specified number of lines.
    ///
    /// # Arguments
    ///
    /// * `count` - The maximum number of lines to read from the input.
    ///
    /// # Returns
    ///
    /// A new instance configured to read at most `count` lines.
    ///
    /// # Examples
    ///
    /// ```
    /// # use keep::filter_plugin::HeadLinesFilter;
    /// let filter = HeadLinesFilter::new(3);
    /// ```
    pub fn new(count: usize) -> Self {
        Self { remaining: count }
    }
}

/// Filters input by reading only the first N lines and writing them to the output.
///
/// Uses buffered line reading to process input line-by-line until the limit or EOF.
///
/// # Arguments
///
/// * `reader` - Mutable reference to the input data stream.
/// * `writer` - Mutable reference to the output stream.
///
/// # Returns
///
/// * `Result<()>` - Success if filtering completes, or I/O error.
///
/// # Errors
///
/// * `io::Error` from line reading or writing operations.
///
/// # Examples
///
/// ```
/// # use std::io::{Read, Write, Cursor};
/// # use keep::filter_plugin::{FilterPlugin, HeadLinesFilter};
/// # let mut filter = HeadLinesFilter::new(2);
/// let mut input: &mut dyn Read = &mut Cursor::new(b"Line1\nLine2\nLine3\n");
/// let mut output = Vec::new();
/// filter.filter(&mut input, &mut output)?;
/// assert_eq!(output, b"Line1\nLine2\n");
/// # Ok::<(), std::io::Error>(())
/// ```
impl FilterPlugin for HeadLinesFilter {
    fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
        if self.remaining == 0 {
            return Ok(());
        }

        let mut buf_reader = std::io::BufReader::new(reader);
        for line in buf_reader.by_ref().lines() {
            let line = line?;
            writeln!(writer, "{line}")?;
            self.remaining -= 1;
            if self.remaining == 0 {
                break;
            }
        }
        Ok(())
    }

    /// Clones this filter into a new boxed instance.
    ///
    /// Creates an independent copy with the same configuration.
    ///
    /// # Returns
    ///
    /// A new `Box<dyn FilterPlugin>` clone.
    ///
    /// # Examples
    ///
    /// ```
    /// # use keep::filter_plugin::{FilterPlugin, HeadLinesFilter};
    /// let filter = HeadLinesFilter::new(5);
    /// let cloned = filter.clone_box();
    /// ```
    fn clone_box(&self) -> Box<dyn FilterPlugin> {
        Box::new(Self {
            remaining: self.remaining,
        })
    }

    /// Returns the configuration options for this filter.
    ///
    /// Defines the "count" parameter as required with no default.
    ///
    /// # Returns
    ///
    /// Vector of `FilterOption` describing parameters.
    ///
    /// # Examples
    ///
    /// ```
    /// # use keep::filter_plugin::{FilterPlugin, HeadLinesFilter};
    /// let filter = HeadLinesFilter::new(5);
    /// let opts = filter.options();
    /// assert_eq!(opts.len(), 1);
    /// assert_eq!(opts[0].name, "count");
    /// assert!(opts[0].required);
    /// ```
    fn options(&self) -> Vec<FilterOption> {
        vec![FilterOption {
            name: "count".to_string(),
            default: None,
            required: true,
        }]
    }

    fn description(&self) -> &str {
        "Read the first N lines"
    }
}

// Register the plugin at module initialization time
#[ctor::ctor]
fn register_head_filters() {
    register_filter_plugin("head_bytes", || Box::new(HeadBytesFilter::new(0)))
        .expect("Failed to register head_bytes filter");
    register_filter_plugin("head_lines", || Box::new(HeadLinesFilter::new(0)))
        .expect("Failed to register head_lines filter");
}
