//! Terminal wrapper around `crossterm`: raw mode + alternate screen, with a
//! `Drop` guard that always restores the terminal on exit.

use std::io::{self, Stdout, Write};

use crossterm::{
    cursor, execute,
    terminal::{
        self, disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen,
        LeaveAlternateScreen,
    },
};

/// Owned handle to the terminal.  Constructing it enters raw mode + alternate
/// screen; dropping it restores both.
pub struct Terminal {
    /// Current width, in columns.
    pub width: u16,
    /// Current height, in rows.
    pub height: u16,
    /// `true` if the terminal almost certainly supports 24-bit / 256-colour output.
    pub supports_256: bool,
    stdout: Stdout,
    active: bool,
}

impl Terminal {
    /// Initialise the terminal: enable raw mode, switch to the alternate screen,
    /// hide the cursor, and clear the display.
    pub fn new() -> io::Result<Self> {
        enable_raw_mode()?;
        let mut stdout = io::stdout();
        execute!(stdout, EnterAlternateScreen, cursor::Hide, Clear(ClearType::All))?;
        let (width, height) = terminal::size()?;
        Ok(Self {
            width,
            height,
            supports_256: detect_truecolor(),
            stdout,
            active: true,
        })
    }

    /// Borrow the underlying `Stdout` for direct `queue!` invocations.
    pub fn out(&mut self) -> &mut Stdout {
        &mut self.stdout
    }

    /// Flush queued output.
    pub fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }
}

fn detect_truecolor() -> bool {
    if let Ok(c) = std::env::var("COLORTERM") {
        if c.contains("truecolor") || c.contains("24bit") {
            return true;
        }
    }
    if let Ok(t) = std::env::var("TERM") {
        if t.contains("256") {
            return true;
        }
        return matches!(
            t.as_str(),
            "xterm-kitty" | "alacritty" | "wezterm" | "rxvt-unicode-256color" | "tmux-256color"
        );
    }
    false
}

impl Drop for Terminal {
    fn drop(&mut self) {
        if !self.active {
            return;
        }
        let _ = execute!(self.stdout, LeaveAlternateScreen, cursor::Show);
        let _ = disable_raw_mode();
        self.active = false;
    }
}
