use alacritty_terminal::event::VoidListener;
use alacritty_terminal::grid::Dimensions;
use alacritty_terminal::index::{Column, Line, Point};
use alacritty_terminal::term::{Config, Term};
use alacritty_terminal::vte::ansi::Processor;

/// Fixed-size terminal dimensions for the daemon-side grid.
#[derive(Clone, Copy)]
pub struct GridSize {
    pub cols: usize,
    pub lines: usize,
}

impl Dimensions for GridSize {
    fn total_lines(&self) -> usize {
        self.lines
    }
    fn screen_lines(&self) -> usize {
        self.lines
    }
    fn columns(&self) -> usize {
        self.cols
    }
}

/// Owns an alacritty terminal model and feeds raw PTY bytes into it.
pub struct GridSurface {
    term: Term<VoidListener>,
    parser: Processor,
    size: GridSize,
}

impl GridSurface {
    pub fn new(cols: u16, rows: u16) -> Self {
        let size = GridSize { cols: cols as usize, lines: rows as usize };
        let term = Term::new(Config::default(), &size, VoidListener);
        Self { term, parser: Processor::new(), size }
    }

    pub fn feed(&mut self, bytes: &[u8]) {
        self.parser.advance(&mut self.term, bytes);
    }

    pub fn resize(&mut self, cols: u16, rows: u16) {
        self.size = GridSize { cols: cols as usize, lines: rows as usize };
        self.term.resize(self.size);
    }

    pub fn size(&self) -> GridSize {
        self.size
    }

    /// Read the visible character at (line, col) — used by tests and the snapshot writer.
    pub fn char_at(&self, line: usize, col: usize) -> char {
        let point = Point::new(Line(line as i32), Column(col));
        self.term.grid()[point].c
    }

    pub fn term(&self) -> &Term<VoidListener> {
        &self.term
    }

    /// The visible grid as text — the last `lines` rows, trailing blanks trimmed.
    /// Used by the fallback detector.
    pub fn tail_text(&self, lines: usize) -> String {
        let size = self.size();
        let start = size.lines.saturating_sub(lines);
        let mut out = String::new();
        for line in start..size.lines {
            let mut row = String::new();
            for col in 0..size.cols {
                row.push(self.char_at(line, col));
            }
            out.push_str(row.trim_end());
            out.push('\n');
        }
        out
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn feeding_plain_text_lands_in_the_grid() {
        let mut g = GridSurface::new(20, 5);
        g.feed(b"hello");
        assert_eq!(g.char_at(0, 0), 'h');
        assert_eq!(g.char_at(0, 4), 'o');
    }

    #[test]
    fn carriage_return_and_newline_move_the_cursor() {
        let mut g = GridSurface::new(20, 5);
        g.feed(b"ab\r\ncd");
        assert_eq!(g.char_at(0, 0), 'a');
        assert_eq!(g.char_at(1, 0), 'c');
    }
}
