use crossterm::event::{KeyCode, KeyEvent};
use ratatui::{
    layout::{Constraint, Layout, Rect},
    style::{Color, Modifier, Style},
    widgets::{Cell, Paragraph, Row, Table, TableState},
    Frame,
};
use sqlx::sqlite::SqlitePool;
use tokio::sync::mpsc;

use crate::app::{AppMessage, NotifyLevel};
use crate::calc::matchup::MatchupResult;
use crate::config::Settings;
use crate::db::models::{BatterCard, Player};
use crate::screens::tier_style;
use crate::widgets::{format_swar, selector::{SelectorEvent, SelectorWidget}};

// =============================================================================
// Types
// =============================================================================

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MatchupFocus {
    TeamSelector,
    PitcherSelector,
    Table,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortMode {
    Rating,
    Name,
    Position,
}

impl SortMode {
    pub fn label(&self) -> &'static str {
        match self {
            SortMode::Rating => "Rating",
            SortMode::Name => "Name",
            SortMode::Position => "Position",
        }
    }
}

// =============================================================================
// State
// =============================================================================

pub struct MatchupState {
    pub team_abbrev: String,
    pub season: i64,

    // Selectors
    pub team_selector: SelectorWidget<i64>,
    pub pitcher_selector: SelectorWidget<i64>,

    // Data
    pub my_batters: Vec<(Player, Option<BatterCard>)>,
    pub matchup_results: Vec<MatchupResult>,
    pub selected_pitcher: Option<Player>,
    pub is_loading_pitchers: bool,
    pub is_calculating: bool,
    pub cache_ready: bool,

    // UI
    pub focus: MatchupFocus,
    pub table_state: TableState,
    pub sort_mode: SortMode,
}

impl MatchupState {
    pub fn new(team_abbrev: String, season: i64) -> Self {
        let mut state = Self {
            team_abbrev,
            season,
            team_selector: SelectorWidget::new("Opponent"),
            pitcher_selector: SelectorWidget::new("Pitcher"),
            my_batters: Vec::new(),
            matchup_results: Vec::new(),
            selected_pitcher: None,
            is_loading_pitchers: false,
            is_calculating: false,
            cache_ready: false,
            focus: MatchupFocus::TeamSelector,
            table_state: TableState::default(),
            sort_mode: SortMode::Rating,
        };
        state.team_selector.is_focused = true;
        state
    }

    pub fn is_input_captured(&self) -> bool {
        self.team_selector.is_open || self.pitcher_selector.is_open
    }

    pub fn mount(&mut self, pool: &SqlitePool, tx: &mpsc::UnboundedSender<AppMessage>) {
        // Ensure score cache
        let pool_c = pool.clone();
        let tx_c = tx.clone();
        tokio::spawn(async move {
            match crate::calc::score_cache::ensure_cache_exists(&pool_c).await {
                Ok(_) => {
                    let _ = tx_c.send(AppMessage::CacheReady);
                }
                Err(e) => {
                    let _ = tx_c.send(AppMessage::CacheError(format!("{e}")));
                }
            }
        });

        // Load teams
        let pool_c = pool.clone();
        let tx_c = tx.clone();
        let season = self.season;
        tokio::spawn(async move {
            match crate::db::queries::get_all_teams(&pool_c, season, true).await {
                Ok(teams) => {
                    let _ = tx_c.send(AppMessage::TeamsLoaded(teams));
                }
                Err(e) => {
                    tracing::error!("Failed to load teams: {e}");
                    let _ = tx_c.send(AppMessage::Notify(
                        format!("Failed to load teams: {e}"),
                        NotifyLevel::Error,
                    ));
                }
            }
        });

        // Load my batters
        let pool_c = pool.clone();
        let tx_c = tx.clone();
        let abbrev = self.team_abbrev.clone();
        let season = self.season;
        tokio::spawn(async move {
            let team = crate::db::queries::get_team_by_abbrev(&pool_c, &abbrev, season).await;
            if let Ok(Some(team)) = team
                && let Ok(batters) =
                    crate::db::queries::get_batters(&pool_c, Some(team.id), Some(season)).await
            {
                let mut with_cards = Vec::with_capacity(batters.len());
                for p in batters {
                    let card = crate::db::queries::get_batter_card(&pool_c, p.id)
                        .await
                        .ok()
                        .flatten();
                    with_cards.push((p, card));
                }
                let _ = tx_c.send(AppMessage::MyBattersLoaded(with_cards));
            }
        });
    }

    pub fn handle_key(
        &mut self,
        key: KeyEvent,
        pool: &SqlitePool,
        _settings: &Settings,
        tx: &mpsc::UnboundedSender<AppMessage>,
    ) {
        // Selectors first
        if self.team_selector.is_open {
            if let SelectorEvent::Selected(_) = self.team_selector.handle_key(key) {
                self.on_team_selected(pool, tx);
            }
            return;
        }
        if self.pitcher_selector.is_open {
            if let SelectorEvent::Selected(_) = self.pitcher_selector.handle_key(key) {
                self.on_pitcher_selected(pool, tx);
            }
            return;
        }

        // Tab: cycle focus
        if key.code == KeyCode::Tab {
            self.cycle_focus();
            return;
        }

        // Focus-specific keys
        match self.focus {
            MatchupFocus::TeamSelector => match key.code {
                KeyCode::Enter | KeyCode::Char(' ') => {
                    self.team_selector.is_open = true;
                    self.team_selector.is_focused = true;
                }
                _ => {}
            },
            MatchupFocus::PitcherSelector => match key.code {
                KeyCode::Enter | KeyCode::Char(' ') => {
                    self.pitcher_selector.is_open = true;
                    self.pitcher_selector.is_focused = true;
                }
                _ => {}
            },
            MatchupFocus::Table => match key.code {
                KeyCode::Char('j') | KeyCode::Down => {
                    let len = self.matchup_results.len();
                    if len > 0 {
                        let i = self.table_state.selected().unwrap_or(0);
                        self.table_state.select(Some((i + 1) % len));
                    }
                }
                KeyCode::Char('k') | KeyCode::Up => {
                    let len = self.matchup_results.len();
                    if len > 0 {
                        let i = self.table_state.selected().unwrap_or(0);
                        self.table_state.select(Some(i.checked_sub(1).unwrap_or(len - 1)));
                    }
                }
                KeyCode::Char('s') => {
                    self.sort_mode = SortMode::Rating;
                    self.sort_results();
                }
                KeyCode::Char('n') => {
                    self.sort_mode = SortMode::Name;
                    self.sort_results();
                }
                KeyCode::Char('p') => {
                    self.sort_mode = SortMode::Position;
                    self.sort_results();
                }
                KeyCode::Char('f') => {
                    self.mount(pool, tx);
                }
                _ => {}
            },
        }
    }

    fn cycle_focus(&mut self) {
        self.team_selector.is_focused = false;
        self.pitcher_selector.is_focused = false;
        self.focus = match self.focus {
            MatchupFocus::TeamSelector => MatchupFocus::PitcherSelector,
            MatchupFocus::PitcherSelector => MatchupFocus::Table,
            MatchupFocus::Table => MatchupFocus::TeamSelector,
        };
        self.team_selector.is_focused = self.focus == MatchupFocus::TeamSelector;
        self.pitcher_selector.is_focused = self.focus == MatchupFocus::PitcherSelector;
    }

    fn sort_results(&mut self) {
        match self.sort_mode {
            SortMode::Rating => {
                self.matchup_results
                    .sort_by(|a, b| b.rating.partial_cmp(&a.rating).unwrap_or(std::cmp::Ordering::Equal));
            }
            SortMode::Name => {
                self.matchup_results.sort_by(|a, b| a.player.name.cmp(&b.player.name));
            }
            SortMode::Position => {
                self.matchup_results.sort_by(|a, b| {
                    let a_pos = a.player.pos_1.as_deref().unwrap_or("ZZ");
                    let b_pos = b.player.pos_1.as_deref().unwrap_or("ZZ");
                    a_pos.cmp(b_pos).then(a.player.name.cmp(&b.player.name))
                });
            }
        }
        if !self.matchup_results.is_empty() {
            self.table_state.select(Some(0));
        }
    }

    fn on_team_selected(&mut self, pool: &SqlitePool, tx: &mpsc::UnboundedSender<AppMessage>) {
        let Some(&team_id) = self.team_selector.selected_value() else {
            return;
        };
        self.pitcher_selector.set_items(Vec::new());
        self.matchup_results.clear();
        self.selected_pitcher = None;
        self.is_loading_pitchers = true;

        let pool = pool.clone();
        let tx = tx.clone();
        let season = self.season;
        tokio::spawn(async move {
            match crate::db::queries::get_pitchers(&pool, Some(team_id), Some(season)).await {
                Ok(pitchers) => {
                    let _ = tx.send(AppMessage::PitchersLoaded(pitchers));
                }
                Err(e) => {
                    tracing::error!("Failed to load pitchers: {e}");
                    let _ = tx.send(AppMessage::Notify(
                        format!("Failed to load pitchers: {e}"),
                        NotifyLevel::Error,
                    ));
                }
            }
        });
    }

    fn on_pitcher_selected(&mut self, pool: &SqlitePool, tx: &mpsc::UnboundedSender<AppMessage>) {
        let Some(&pitcher_id) = self.pitcher_selector.selected_value() else {
            return;
        };
        self.is_calculating = true;
        self.matchup_results.clear();

        let pool = pool.clone();
        let tx = tx.clone();
        let batters = self.my_batters.clone();
        tokio::spawn(async move {
            let pitcher = crate::db::queries::get_player_by_id(&pool, pitcher_id).await;
            let Ok(Some(pitcher)) = pitcher else { return };
            let pitcher_card = crate::db::queries::get_pitcher_card(&pool, pitcher_id)
                .await
                .ok()
                .flatten();

            match crate::calc::matchup::calculate_team_matchups_cached(
                &pool,
                &batters,
                &pitcher,
                pitcher_card.as_ref(),
            )
            .await
            {
                Ok(results) => {
                    let _ = tx.send(AppMessage::MatchupsCalculated(results));
                }
                Err(e) => {
                    tracing::error!("Matchup calculation failed: {e}");
                    let _ = tx.send(AppMessage::Notify(
                        format!("Matchup calculation failed: {e}"),
                        NotifyLevel::Error,
                    ));
                }
            }
        });
    }

    pub fn handle_message(
        &mut self,
        msg: AppMessage,
        _pool: &SqlitePool,
        _settings: &Settings,
        _tx: &mpsc::UnboundedSender<AppMessage>,
    ) {
        match msg {
            AppMessage::CacheReady => {
                self.cache_ready = true;
            }
            AppMessage::CacheError(e) => {
                self.cache_ready = false;
                tracing::warn!("Cache error: {e}");
            }
            AppMessage::TeamsLoaded(teams) => {
                let items: Vec<(String, i64)> = teams
                    .into_iter()
                    .filter(|t| t.abbrev != self.team_abbrev)
                    .map(|t| (format!("{} ({})", t.abbrev, t.short_name), t.id))
                    .collect();
                self.team_selector.set_items(items);
            }
            AppMessage::MyBattersLoaded(batters) => {
                self.my_batters = batters;
            }
            AppMessage::PitchersLoaded(pitchers) => {
                self.is_loading_pitchers = false;
                let items: Vec<(String, i64)> = pitchers
                    .into_iter()
                    .map(|p| {
                        let hand = p.hand.as_deref().unwrap_or("?");
                        (format!("{} ({})", p.name, hand), p.id)
                    })
                    .collect();
                self.pitcher_selector.set_items(items);
            }
            AppMessage::MatchupsCalculated(results) => {
                self.is_calculating = false;
                self.matchup_results = results;
                self.sort_results();
            }
            _ => {}
        }
    }

    // =========================================================================
    // Rendering
    // =========================================================================

    pub fn render(&self, frame: &mut Frame, area: Rect, tick_count: u64) {
        let chunks = Layout::vertical([
            Constraint::Length(3), // team selector
            Constraint::Length(3), // pitcher selector
            Constraint::Length(1), // pitcher info
            Constraint::Min(0),   // table
            Constraint::Length(1), // hints
        ])
        .split(area);

        self.team_selector.render_closed(frame, chunks[0]);
        self.pitcher_selector.render_closed(frame, chunks[1]);
        self.render_pitcher_info(frame, chunks[2]);
        self.render_table(frame, chunks[3], tick_count);
        self.render_hints(frame, chunks[4]);

        // Selector overlays
        if self.team_selector.is_open {
            self.team_selector.render_popup(frame, chunks[0]);
        }
        if self.pitcher_selector.is_open {
            self.pitcher_selector.render_popup(frame, chunks[1]);
        }
    }

    fn render_pitcher_info(&self, frame: &mut Frame, area: Rect) {
        let text = if let Some(pitcher) = &self.selected_pitcher {
            let hand = pitcher.hand.as_deref().unwrap_or("?");
            let pos = pitcher.positions().join("/");
            format!(" vs {} ({}HP) — {}", pitcher.name, hand, pos)
        } else {
            String::new()
        };
        let widget = Paragraph::new(text).style(Style::default().fg(Color::White));
        frame.render_widget(widget, area);
    }

    fn render_table(&self, frame: &mut Frame, area: Rect, tick_count: u64) {
        if self.is_calculating {
            let dots = ".".repeat((tick_count as usize / 2) % 4);
            let loading = Paragraph::new(format!(" Calculating matchups{}", dots))
                .style(Style::default().fg(Color::DarkGray));
            frame.render_widget(loading, area);
            return;
        }

        if self.matchup_results.is_empty() {
            let msg = if self.team_selector.selected_value().is_none() {
                " Select an opponent team to begin"
            } else if self.pitcher_selector.selected_value().is_none() {
                " Select a pitcher to calculate matchups"
            } else {
                " No matchup results"
            };
            let widget = Paragraph::new(msg).style(Style::default().fg(Color::DarkGray));
            frame.render_widget(widget, area);
            return;
        }

        let header = Row::new(vec![
            Cell::from("#"),
            Cell::from("Name"),
            Cell::from("H"),
            Cell::from("Pos"),
            Cell::from("Rating"),
            Cell::from("Tier"),
            Cell::from("sWAR"),
            Cell::from("Split"),
        ])
        .style(Style::default().add_modifier(Modifier::BOLD).fg(Color::White));

        let rows: Vec<Row> = self
            .matchup_results
            .iter()
            .enumerate()
            .map(|(i, r)| {
                let tier_style = tier_style(&r.tier);
                Row::new(vec![
                    Cell::from((i + 1).to_string()),
                    Cell::from(r.player.name.clone()),
                    Cell::from(r.batter_hand.clone()),
                    Cell::from(r.player.positions().join("/")),
                    Cell::from(r.rating_display()).style(tier_style),
                    Cell::from(r.tier.clone()).style(tier_style),
                    Cell::from(format_swar(r.player.swar)),
                    Cell::from(r.batter_split.clone()),
                ])
            })
            .collect();

        let widths = [
            Constraint::Length(3),  // #
            Constraint::Length(20), // Name
            Constraint::Length(3),  // Hand
            Constraint::Length(14), // Pos
            Constraint::Length(7),  // Rating
            Constraint::Length(5),  // Tier
            Constraint::Length(6),  // sWAR
            Constraint::Min(0),    // Split
        ];

        let table = Table::new(rows, widths)
            .header(header)
            .row_highlight_style(Style::default().bg(Color::DarkGray));

        let mut table_state = self.table_state;
        frame.render_stateful_widget(table, area, &mut table_state);
    }

    fn render_hints(&self, frame: &mut Frame, area: Rect) {
        let hints = Paragraph::new(format!(
            " [Tab] Focus  [j/k] Scroll  Sort: [s]core [n]ame [p]os ({})  [f] Refresh",
            self.sort_mode.label()
        ))
        .style(Style::default().fg(Color::DarkGray));
        frame.render_widget(hints, area);
    }
}


