use std::collections::VecDeque;
use std::hash::{DefaultHasher, Hash, Hasher};

use ansi_to_tui::IntoText;
use ratatui::style::Color;
use ratatui::text::Line;

const MAX_LINES: usize = 10_000;

const COLOR_PALETTE: &[Color] = &[
    Color::Red,
    Color::Green,
    Color::Yellow,
    Color::Blue,
    Color::Magenta,
    Color::Cyan,
    Color::LightRed,
    Color::LightGreen,
];

pub struct Shell {
    id: usize,
    name: String,
    color: Color,
    lines: VecDeque<Line<'static>>,
    scroll_offset: usize,
}

impl Shell {
    pub fn new(id: usize, name: String) -> Self {
        let color = Self::color_for_name(&name);
        Self {
            id,
            name,
            color,
            lines: VecDeque::new(),
            scroll_offset: 0,
        }
    }

    pub fn id(&self) -> usize {
        self.id
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn color(&self) -> Color {
        self.color
    }

    pub fn line_count(&self) -> usize {
        self.lines.len()
    }

    pub fn is_auto_scroll(&self) -> bool {
        self.scroll_offset == 0
    }

    pub fn push_raw_line(&mut self, raw: &[u8]) {
        let line = raw
            .into_text()
            .ok()
            .and_then(|text| text.lines.into_iter().next())
            .unwrap_or_else(|| Line::raw(String::from_utf8_lossy(raw).into_owned()));

        self.lines.push_back(line);
        if self.lines.len() > MAX_LINES {
            self.lines.pop_front();
            if self.scroll_offset > 0 {
                self.scroll_offset = self.scroll_offset.saturating_sub(1);
            }
        }
    }

    pub fn scroll_up(&mut self, amount: usize) {
        let max_offset = self.lines.len().saturating_sub(1);
        self.scroll_offset = (self.scroll_offset + amount).min(max_offset);
    }

    pub fn scroll_down(&mut self, amount: usize) {
        self.scroll_offset = self.scroll_offset.saturating_sub(amount);
    }

    pub fn scroll_to_top(&mut self) {
        self.scroll_offset = self.lines.len().saturating_sub(1);
    }

    pub fn scroll_to_bottom(&mut self) {
        self.scroll_offset = 0;
    }

    pub fn visible_lines(&self, viewport_height: usize) -> impl Iterator<Item = &Line<'static>> {
        let total = self.lines.len();
        let end = total.saturating_sub(self.scroll_offset);
        let start = end.saturating_sub(viewport_height);
        self.lines.range(start..end)
    }

    fn color_for_name(name: &str) -> Color {
        let mut hasher = DefaultHasher::new();
        name.hash(&mut hasher);
        let idx = (hasher.finish() as usize) % COLOR_PALETTE.len();
        COLOR_PALETTE[idx]
    }
}
