//! Full-screen TUI application for terminal charting.
//!
//! Provides a multi-tab interface with Chart, Watchlist, and Help views.

use std::io::Write;
use std::time::Duration;

use crossterm::{
    cursor,
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    terminal::{self, BeginSynchronizedUpdate, ClearType, EndSynchronizedUpdate},
};

use termichart_core::OverlayKind;
use termichart_data::ExchangeClient;
use crate::ChartBuilder;

/// Active tab in the TUI.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Tab {
    Chart,
    Watchlist,
    Help,
}

impl Tab {
    fn label(&self) -> &str {
        match self {
            Tab::Chart => "Chart",
            Tab::Watchlist => "Watchlist",
            Tab::Help => "Help",
        }
    }

    fn all() -> &'static [Tab] {
        &[Tab::Chart, Tab::Watchlist, Tab::Help]
    }

    fn next(&self) -> Tab {
        match self {
            Tab::Chart => Tab::Watchlist,
            Tab::Watchlist => Tab::Help,
            Tab::Help => Tab::Chart,
        }
    }

    fn prev(&self) -> Tab {
        match self {
            Tab::Chart => Tab::Help,
            Tab::Watchlist => Tab::Chart,
            Tab::Help => Tab::Watchlist,
        }
    }
}

/// Full-screen TUI application.
pub struct TuiApp {
    symbols: Vec<String>,
    active_tab: Tab,
    active_symbol_idx: usize,
    chart_interval: String,
}

impl TuiApp {
    /// Create a new TUI app with default crypto symbols.
    pub fn new() -> Self {
        Self {
            symbols: vec![
                "BTCUSDT".to_string(),
                "ETHUSDT".to_string(),
                "SOLUSDT".to_string(),
                "BNBUSDT".to_string(),
                "ADAUSDT".to_string(),
                "DOGEUSDT".to_string(),
            ],
            active_tab: Tab::Chart,
            active_symbol_idx: 0,
            chart_interval: "1m".to_string(),
        }
    }

    /// Create a TUI app with custom symbols.
    pub fn with_symbols(symbols: Vec<String>) -> Self {
        Self {
            symbols,
            active_tab: Tab::Chart,
            active_symbol_idx: 0,
            chart_interval: "1m".to_string(),
        }
    }

    /// Run the TUI application (blocking).
    pub fn run(&mut self) -> std::result::Result<(), Box<dyn std::error::Error>> {
        let mut stdout = std::io::stdout();
        let client = ExchangeClient::binance();

        terminal::enable_raw_mode()?;
        execute!(
            stdout,
            terminal::EnterAlternateScreen,
            cursor::Hide,
            terminal::Clear(ClearType::All)
        )?;

        let poll = Duration::from_secs(3);

        loop {
            // Get terminal size
            let (term_w, term_h) = terminal::size().unwrap_or((80, 24));

            // Check input
            if event::poll(poll)? {
                if let Event::Key(KeyEvent { code, modifiers, .. }) = event::read()? {
                    match code {
                        KeyCode::Char('q') | KeyCode::Esc => break,
                        KeyCode::Char('c') if modifiers.contains(KeyModifiers::CONTROL) => break,
                        KeyCode::Tab => self.active_tab = self.active_tab.next(),
                        KeyCode::BackTab => self.active_tab = self.active_tab.prev(),
                        KeyCode::Char('1') => self.active_tab = Tab::Chart,
                        KeyCode::Char('2') => self.active_tab = Tab::Watchlist,
                        KeyCode::Char('3') => self.active_tab = Tab::Help,
                        KeyCode::Up => {
                            if self.active_symbol_idx > 0 {
                                self.active_symbol_idx -= 1;
                            }
                        }
                        KeyCode::Down => {
                            if self.active_symbol_idx < self.symbols.len().saturating_sub(1) {
                                self.active_symbol_idx += 1;
                            }
                        }
                        _ => {}
                    }
                }
            }

            queue!(stdout, BeginSynchronizedUpdate, cursor::MoveTo(0, 0))?;

            // Draw tab bar
            let tab_bar = self.render_tab_bar(term_w);
            write!(stdout, "{}\r\n", tab_bar)?;

            // Draw separator
            write!(stdout, "\x1b[90m{}\x1b[0m\r\n", "\u{2500}".repeat(term_w as usize))?;

            let content_height = term_h.saturating_sub(4); // tab bar + separator + status bar

            match self.active_tab {
                Tab::Chart => {
                    self.render_chart_tab(&mut stdout, &client, term_w, content_height)?;
                }
                Tab::Watchlist => {
                    self.render_watchlist_tab(&mut stdout, &client, term_w, content_height)?;
                }
                Tab::Help => {
                    self.render_help_tab(&mut stdout, term_w, content_height)?;
                }
            }

            // Status bar at bottom
            let status = format!(
                "\x1b[7m {} \x1b[0m  \x1b[2mTab: switch | 1-3: tabs | \u{2191}\u{2193}: select | q: quit\x1b[0m\x1b[K",
                self.symbols.get(self.active_symbol_idx).unwrap_or(&"---".to_string())
            );
            execute!(stdout, cursor::MoveTo(0, term_h - 1))?;
            write!(stdout, "{}", status)?;

            queue!(stdout, EndSynchronizedUpdate)?;
            stdout.flush()?;
        }

        execute!(
            stdout,
            terminal::LeaveAlternateScreen,
            cursor::Show
        )?;
        terminal::disable_raw_mode()?;
        println!("TUI session ended.");

        Ok(())
    }

    fn render_tab_bar(&self, width: u16) -> String {
        let mut bar = String::new();
        for (i, tab) in Tab::all().iter().enumerate() {
            if *tab == self.active_tab {
                bar.push_str(&format!("\x1b[1;7m {} {} \x1b[0m ", i + 1, tab.label()));
            } else {
                bar.push_str(&format!("\x1b[2m {} {} \x1b[0m ", i + 1, tab.label()));
            }
        }
        // Pad to width
        let visible_len = Tab::all().len() * 14; // approximate
        if (visible_len as u16) < width {
            bar.push_str(&" ".repeat((width as usize).saturating_sub(visible_len)));
        }
        bar
    }

    fn render_chart_tab(
        &self,
        stdout: &mut impl Write,
        client: &ExchangeClient,
        width: u16,
        height: u16,
    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
        let symbol = match self.symbols.get(self.active_symbol_idx) {
            Some(s) => s,
            None => {
                self.render_empty(stdout, height)?;
                return Ok(());
            }
        };

        let candles = client.fetch_candles(symbol, &self.chart_interval, 40);

        match candles {
            Some(candles) if !candles.is_empty() => {
                let last = candles.last().unwrap();
                let bull = last.close >= last.open;
                let arrow = if bull { "\u{25B2}" } else { "\u{25BC}" };

                let sym_display = format_tui_symbol(symbol);
                let title = format!(
                    "{} {}  {}  ${:.2}",
                    sym_display, self.chart_interval, arrow, last.close
                );

                let chart_h = height.saturating_sub(1);
                let output = ChartBuilder::candlestick()
                    .size(width, chart_h)
                    .title(title)
                    .x_axis_time()
                    .heikin_ashi()
                    .overlay(OverlayKind::Sma(7))
                    .candles(candles)
                    .render();

                match output {
                    Ok(s) => {
                        let s = s.replace('\n', "\r\n");
                        stdout.write_all(s.as_bytes())?;
                    }
                    Err(_) => {
                        self.render_empty(stdout, height)?;
                    }
                }
            }
            _ => {
                write!(stdout, "  Loading {}...\r\n", symbol)?;
                for _ in 1..height {
                    write!(stdout, "\x1b[K\r\n")?;
                }
            }
        }

        Ok(())
    }

    fn render_watchlist_tab(
        &self,
        stdout: &mut impl Write,
        client: &ExchangeClient,
        width: u16,
        height: u16,
    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
        // Header
        write!(
            stdout,
            "\x1b[1m  {:<12} {:>14} {:>12}\x1b[0m\x1b[K\r\n",
            "SYMBOL", "PRICE", "STATUS"
        )?;
        write!(stdout, "  \x1b[90m{}\x1b[0m\x1b[K\r\n", "\u{2500}".repeat((width as usize).saturating_sub(4)))?;

        let mut rows_used = 2u16;

        for (i, symbol) in self.symbols.iter().enumerate() {
            if rows_used >= height {
                break;
            }

            let price = client.fetch_spot_price(symbol);
            let sym_display = format_tui_symbol(symbol);

            let selected = if i == self.active_symbol_idx { "\x1b[7m" } else { "" };
            let reset = if i == self.active_symbol_idx { "\x1b[0m" } else { "" };

            match price {
                Some(p) => {
                    write!(
                        stdout,
                        "  {}{:<12} {:>14.2} {:>12}{}\x1b[K\r\n",
                        selected, sym_display, p, "\u{2713} live", reset
                    )?;
                }
                None => {
                    write!(
                        stdout,
                        "  {}{:<12} {:>14} {:>12}{}\x1b[K\r\n",
                        selected, sym_display, "---", "offline", reset
                    )?;
                }
            }
            rows_used += 1;
        }

        // Fill remaining
        while rows_used < height {
            write!(stdout, "\x1b[K\r\n")?;
            rows_used += 1;
        }

        Ok(())
    }

    fn render_help_tab(
        &self,
        stdout: &mut impl Write,
        _width: u16,
        height: u16,
    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
        let help_lines = [
            "",
            "  \x1b[1mTermiChart TUI\x1b[0m",
            "",
            "  \x1b[1mNavigation:\x1b[0m",
            "    Tab / Shift+Tab    Switch between tabs",
            "    1, 2, 3            Jump to tab directly",
            "    Up / Down          Select symbol",
            "",
            "  \x1b[1mActions:\x1b[0m",
            "    q / Esc            Quit",
            "    Ctrl+C             Force quit",
            "",
            "  \x1b[1mTabs:\x1b[0m",
            "    1. Chart      Live candlestick chart for selected symbol",
            "    2. Watchlist   Price overview of all symbols",
            "    3. Help        This help screen",
            "",
            "  \x1b[2mPowered by TermiChart SDK\x1b[0m",
        ];

        let mut rows = 0u16;
        for line in &help_lines {
            if rows >= height {
                break;
            }
            write!(stdout, "{}\x1b[K\r\n", line)?;
            rows += 1;
        }

        while rows < height {
            write!(stdout, "\x1b[K\r\n")?;
            rows += 1;
        }

        Ok(())
    }

    fn render_empty(
        &self,
        stdout: &mut impl Write,
        height: u16,
    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
        for _ in 0..height {
            write!(stdout, "\x1b[K\r\n")?;
        }
        Ok(())
    }
}

fn format_tui_symbol(sym: &str) -> String {
    let s = sym.to_uppercase();
    for base in &["USDT", "BUSD", "USD", "BTC", "ETH", "BNB"] {
        if s.ends_with(base) && s.len() > base.len() {
            let prefix = &s[..s.len() - base.len()];
            return format!("{}/{}", prefix, base);
        }
    }
    s
}

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

    #[test]
    fn tab_navigation() {
        assert_eq!(Tab::Chart.next(), Tab::Watchlist);
        assert_eq!(Tab::Watchlist.next(), Tab::Help);
        assert_eq!(Tab::Help.next(), Tab::Chart);
        assert_eq!(Tab::Chart.prev(), Tab::Help);
    }

    #[test]
    fn tui_creation() {
        let tui = TuiApp::new();
        assert_eq!(tui.symbols.len(), 6);
        assert_eq!(tui.active_tab, Tab::Chart);
    }

    #[test]
    fn tui_with_symbols() {
        let tui = TuiApp::with_symbols(vec!["BTCUSDT".to_string()]);
        assert_eq!(tui.symbols.len(), 1);
    }

    #[test]
    fn format_symbol() {
        assert_eq!(format_tui_symbol("BTCUSDT"), "BTC/USDT");
        assert_eq!(format_tui_symbol("ETHUSDT"), "ETH/USDT");
    }

    #[test]
    fn tab_labels() {
        assert_eq!(Tab::Chart.label(), "Chart");
        assert_eq!(Tab::Watchlist.label(), "Watchlist");
        assert_eq!(Tab::Help.label(), "Help");
    }
}
