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

/// A filter that reads the last N bytes from the input stream.
pub struct TailBytesFilter {
    buffer: VecDeque<u8>,
    count: usize,
}

impl TailBytesFilter {
    /// Creates a new `TailBytesFilter` that will keep the last specified number of bytes.
    ///
    /// # Arguments
    ///
    /// * `count` - The number of bytes to retain from the end of the input.
    pub fn new(count: usize) -> Self {
        Self {
            buffer: VecDeque::with_capacity(count),
            count,
        }
    }
}

impl FilterPlugin for TailBytesFilter {
    /// Filters the input by keeping only the last N bytes and writing them to the output.
    ///
    /// # Arguments
    ///
    /// * `reader` - Mutable reference to the input reader providing the data stream.
    /// * `writer` - Mutable reference to the output writer where filtered data is sent.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` on success, or an `io::Error` if reading or writing fails.
    fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
        let mut temp_buffer = vec![0; PIPESIZE];
        loop {
            let bytes_read = reader.read(&mut temp_buffer)?;
            if bytes_read == 0 {
                break;
            }

            // Add new data to the buffer
            for &byte in &temp_buffer[..bytes_read] {
                if self.buffer.len() == self.count {
                    self.buffer.pop_front();
                }
                self.buffer.push_back(byte);
            }
        }

        // Write the buffered data at the end
        let result: Vec<u8> = self.buffer.iter().cloned().collect();
        writer.write_all(&result)?;
        Ok(())
    }

    /// Clones this filter into a new boxed instance.
    ///
    /// # Returns
    ///
    /// A new `Box<dyn FilterPlugin>` representing a clone of this filter.
    fn clone_box(&self) -> Box<dyn FilterPlugin> {
        Box::new(Self {
            buffer: self.buffer.clone(),
            count: self.count,
        })
    }

    /// Returns the configuration options for this filter.
    ///
    /// # Returns
    ///
    /// A vector of `FilterOption` describing the filter's configurable parameters.
    fn options(&self) -> Vec<FilterOption> {
        vec![FilterOption {
            name: "count".to_string(),
            default: None,
            required: true,
        }]
    }

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

/// A filter that reads the last N lines from the input stream.
pub struct TailLinesFilter {
    lines: VecDeque<String>,
    count: usize,
}

impl TailLinesFilter {
    /// Creates a new `TailLinesFilter` that will keep the last specified number of lines.
    ///
    /// # Arguments
    ///
    /// * `count` - The number of lines to retain from the end of the input.
    pub fn new(count: usize) -> Self {
        Self {
            lines: VecDeque::with_capacity(count),
            count,
        }
    }
}

impl FilterPlugin for TailLinesFilter {
    /// Filters the input by keeping only the last N lines and writing them to the output.
    ///
    /// # Arguments
    ///
    /// * `reader` - Mutable reference to the input reader providing the data stream.
    /// * `writer` - Mutable reference to the output writer where filtered data is sent.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` on success, or an `io::Error` if reading or writing fails.
    fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
        let mut buf_reader = std::io::BufReader::new(reader);
        for line in buf_reader.by_ref().lines() {
            let line = line?;
            if self.lines.len() == self.count {
                self.lines.pop_front();
            }
            self.lines.push_back(line);
        }

        // Write the buffered lines
        for line in &self.lines {
            writeln!(writer, "{line}")?;
        }
        Ok(())
    }

    /// Clones this filter into a new boxed instance.
    ///
    /// # Returns
    ///
    /// A new `Box<dyn FilterPlugin>` representing a clone of this filter.
    fn clone_box(&self) -> Box<dyn FilterPlugin> {
        Box::new(Self {
            lines: self.lines.clone(),
            count: self.count,
        })
    }

    /// Returns the configuration options for this filter.
    ///
    /// # Returns
    ///
    /// A vector of `FilterOption` describing the filter's configurable parameters.
    fn options(&self) -> Vec<FilterOption> {
        vec![FilterOption {
            name: "count".to_string(),
            default: None,
            required: true,
        }]
    }

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

// Register the plugin at module initialization time
#[ctor::ctor]
fn register_tail_filters() {
    register_filter_plugin("tail_bytes", || Box::new(TailBytesFilter::new(0)))
        .expect("Failed to register tail_bytes filter");
    register_filter_plugin("tail_lines", || Box::new(TailLinesFilter::new(0)))
        .expect("Failed to register tail_lines filter");
}
