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

use crate::app::{AppMessage, NotifyLevel};
use crate::config::Settings;
use crate::db::models::Player;
use crate::widgets::{format_rating, format_swar};

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

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RosterTab {
    Majors,
    Minors,
    IL,
}

#[derive(Debug, Clone)]
pub struct RosterRow {
    pub player: Player,
    pub rating_vl: Option<f64>,
    pub rating_vr: Option<f64>,
    pub rating_overall: Option<f64>,
    pub endurance_start: Option<i64>,
    pub endurance_relief: Option<i64>,
    pub endurance_close: Option<i64>,
}

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

pub struct RosterState {
    pub team_abbrev: String,
    pub season: i64,
    pub major_slots: usize,
    pub minor_slots: usize,

    // Data
    pub majors: Vec<RosterRow>,
    pub minors: Vec<RosterRow>,
    pub il: Vec<RosterRow>,
    pub swar_total: f64,
    pub swar_cap: Option<f64>,
    pub team_name: String,
    pub is_loading: bool,

    // UI
    pub active_tab: RosterTab,
    pub table_state: TableState,
}

impl RosterState {
    pub fn new(team_abbrev: String, season: i64, settings: &Settings) -> Self {
        Self {
            team_abbrev,
            season,
            major_slots: settings.team.major_league_slots,
            minor_slots: settings.team.minor_league_slots,
            majors: Vec::new(),
            minors: Vec::new(),
            il: Vec::new(),
            swar_total: 0.0,
            swar_cap: None,
            team_name: String::new(),
            is_loading: true,
            active_tab: RosterTab::Majors,
            table_state: TableState::default(),
        }
    }

    pub fn mount(&mut self, pool: &SqlitePool, tx: &mpsc::UnboundedSender<AppMessage>) {
        self.is_loading = true;

        // Load team info
        {
            let pool = pool.clone();
            let tx = tx.clone();
            let abbrev = self.team_abbrev.clone();
            let season = self.season;
            tokio::spawn(async move {
                match crate::db::queries::get_team_by_abbrev(&pool, &abbrev, season).await {
                    Ok(Some(team)) => {
                        let name = if team.long_name.is_empty() {
                            team.short_name.clone()
                        } else {
                            team.long_name.clone()
                        };
                        let _ = tx.send(AppMessage::TeamInfoLoaded(name, team.salary_cap));
                    }
                    Ok(None) => {
                        let _ = tx.send(AppMessage::TeamInfoLoaded(abbrev, None));
                    }
                    Err(e) => {
                        tracing::error!("Failed to load team info: {e}");
                    }
                }
            });
        }

        // Load roster with card data
        {
            let pool = pool.clone();
            let tx = tx.clone();
            let abbrev = self.team_abbrev.clone();
            let season = self.season;
            tokio::spawn(async move {
                match crate::db::queries::get_my_roster(&pool, &abbrev, season).await {
                    Ok(roster) => {
                        let majors = build_roster_rows(&pool, &roster.majors).await;
                        let minors = build_roster_rows(&pool, &roster.minors).await;
                        let il = build_roster_rows(&pool, &roster.il).await;
                        let swar_total = majors.iter().filter_map(|r| r.player.swar).sum();

                        let _ = tx.send(AppMessage::RosterPlayersLoaded {
                            majors,
                            minors,
                            il,
                            swar_total,
                        });
                    }
                    Err(e) => {
                        tracing::error!("Failed to load roster: {e}");
                        let _ = tx.send(AppMessage::Notify(
                            format!("Failed to load roster: {e}"),
                            NotifyLevel::Error,
                        ));
                    }
                }
            });
        }
    }

    pub fn handle_key(
        &mut self,
        key: KeyEvent,
        pool: &SqlitePool,
        tx: &mpsc::UnboundedSender<AppMessage>,
    ) {
        match key.code {
            KeyCode::Char('1') => {
                self.active_tab = RosterTab::Majors;
                self.table_state = TableState::default();
            }
            KeyCode::Char('2') => {
                self.active_tab = RosterTab::Minors;
                self.table_state = TableState::default();
            }
            KeyCode::Char('3') => {
                self.active_tab = RosterTab::IL;
                self.table_state = TableState::default();
            }
            KeyCode::Char('j') | KeyCode::Down => {
                let len = self.active_rows().len();
                if len > 0 {
                    let i = self.table_state.selected().map_or(0, |i| (i + 1).min(len - 1));
                    self.table_state.select(Some(i));
                }
            }
            KeyCode::Char('k') | KeyCode::Up => {
                let len = self.active_rows().len();
                if len > 0 {
                    let i = self.table_state.selected().map_or(0, |i| i.saturating_sub(1));
                    self.table_state.select(Some(i));
                }
            }
            KeyCode::Char('f') => {
                self.mount(pool, tx);
            }
            _ => {}
        }
    }

    pub fn handle_message(&mut self, msg: AppMessage) {
        match msg {
            AppMessage::RosterPlayersLoaded {
                majors,
                minors,
                il,
                swar_total,
            } => {
                self.majors = majors;
                self.minors = minors;
                self.il = il;
                // Sort each list: batters first, then pitchers (matches render order)
                for list in [&mut self.majors, &mut self.minors, &mut self.il] {
                    list.sort_by_key(|r| if r.player.is_batter() { 0 } else { 1 });
                }
                self.swar_total = swar_total;
                self.is_loading = false;
                self.table_state = TableState::default();
            }
            AppMessage::TeamInfoLoaded(name, cap) => {
                self.team_name = name;
                self.swar_cap = cap;
            }
            _ => {}
        }
    }

    fn active_rows(&self) -> &[RosterRow] {
        match self.active_tab {
            RosterTab::Majors => &self.majors,
            RosterTab::Minors => &self.minors,
            RosterTab::IL => &self.il,
        }
    }

    pub fn render(&self, frame: &mut Frame, area: Rect) {
        let chunks = Layout::vertical([
            Constraint::Length(1), // title
            Constraint::Length(1), // tabs
            Constraint::Min(0),   // table
            Constraint::Length(1), // hints
        ])
        .split(area);

        self.render_title(frame, chunks[0]);
        self.render_tabs(frame, chunks[1]);
        self.render_table(frame, chunks[2]);
        self.render_hints(frame, chunks[3]);
    }

    fn render_title(&self, frame: &mut Frame, area: Rect) {
        let cap_str = match self.swar_cap {
            Some(cap) => format!(" / {:.2}", cap),
            None => String::new(),
        };
        let title = format!(
            " Roster: {} — sWAR: {:.2}{}",
            if self.team_name.is_empty() {
                &self.team_abbrev
            } else {
                &self.team_name
            },
            self.swar_total,
            cap_str,
        );
        let widget = Paragraph::new(title)
            .style(Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD));
        frame.render_widget(widget, area);
    }

    fn render_tabs(&self, frame: &mut Frame, area: Rect) {
        let tab_style = |tab: RosterTab| {
            if self.active_tab == tab {
                Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)
            } else {
                Style::default().fg(Color::DarkGray)
            }
        };

        let spans = vec![
            Span::styled(
                format!(" [1] Majors ({}/{})", self.majors.len(), self.major_slots),
                tab_style(RosterTab::Majors),
            ),
            Span::styled(
                format!(" [2] Minors ({}/{})", self.minors.len(), self.minor_slots),
                tab_style(RosterTab::Minors),
            ),
            Span::styled(
                format!(" [3] IL ({})", self.il.len()),
                tab_style(RosterTab::IL),
            ),
        ];

        frame.render_widget(Paragraph::new(Line::from(spans)), area);
    }

    fn render_table(&self, frame: &mut Frame, area: Rect) {
        let rows_data = self.active_rows();

        if self.is_loading {
            let loading = Paragraph::new(" Loading roster...")
                .style(Style::default().fg(Color::DarkGray));
            frame.render_widget(loading, area);
            return;
        }

        if rows_data.is_empty() {
            let empty = Paragraph::new(" No players")
                .style(Style::default().fg(Color::DarkGray));
            frame.render_widget(empty, area);
            return;
        }

        // Split into batters and pitchers (rows are pre-sorted: batters first)
        let (batters, pitchers): (Vec<&RosterRow>, Vec<&RosterRow>) =
            rows_data.iter().partition(|r| r.player.is_batter());

        // Map combined selection index to the correct sub-table
        let selected = self.table_state.selected();
        let batter_count = batters.len();
        let (batter_selected, pitcher_selected) = match selected {
            Some(i) if i < batter_count => (Some(i), None),
            Some(i) => (None, Some(i - batter_count)),
            None => (None, None),
        };

        // Calculate split area
        let batter_height = batters.len() as u16 + 2; // +2 for header + section label
        let available = area.height;

        let table_chunks = Layout::vertical([
            Constraint::Length(1),                    // "Batters" label
            Constraint::Length(batter_height.min(available / 2)), // batter table
            Constraint::Length(1),                    // "Pitchers" label
            Constraint::Min(0),                       // pitcher table
        ])
        .split(area);

        // Batter section label
        frame.render_widget(
            Paragraph::new(" Batters")
                .style(Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)),
            table_chunks[0],
        );

        // Batter table
        let batter_header = Row::new(vec![
            Cell::from("Name"),
            Cell::from("H"),
            Cell::from("Pos"),
            Cell::from("vL"),
            Cell::from("vR"),
            Cell::from("Ovr"),
            Cell::from("sWAR"),
            Cell::from("Fld"),
        ])
        .style(Style::default().add_modifier(Modifier::BOLD).fg(Color::White));

        let batter_rows: Vec<Row> = batters
            .iter()
            .map(|r| {
                Row::new(vec![
                    Cell::from(r.player.name.clone()),
                    Cell::from(r.player.hand.clone().unwrap_or_default()),
                    Cell::from(r.player.positions().join("/")),
                    Cell::from(format_rating(r.rating_vl)),
                    Cell::from(format_rating(r.rating_vr)),
                    Cell::from(format_rating(r.rating_overall)),
                    Cell::from(format_swar(r.player.swar)),
                    Cell::from(String::new()),
                ])
            })
            .collect();

        let batter_table = Table::new(
            batter_rows,
            [
                Constraint::Length(20), // Name
                Constraint::Length(3),  // Hand
                Constraint::Length(14), // Pos
                Constraint::Length(6),  // vL
                Constraint::Length(6),  // vR
                Constraint::Length(6),  // Ovr
                Constraint::Length(6),  // sWAR
                Constraint::Min(0),    // Fld
            ],
        )
        .header(batter_header)
        .row_highlight_style(Style::default().bg(Color::DarkGray));

        let mut batter_state = TableState::default();
        batter_state.select(batter_selected);
        frame.render_stateful_widget(batter_table, table_chunks[1], &mut batter_state);

        // Pitcher section label
        frame.render_widget(
            Paragraph::new(" Pitchers")
                .style(Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD)),
            table_chunks[2],
        );

        // Pitcher table
        let pitcher_header = Row::new(vec![
            Cell::from("Name"),
            Cell::from("H"),
            Cell::from("Pos"),
            Cell::from("vL"),
            Cell::from("vR"),
            Cell::from("Ovr"),
            Cell::from("sWAR"),
            Cell::from("S"),
            Cell::from("R"),
            Cell::from("C"),
        ])
        .style(Style::default().add_modifier(Modifier::BOLD).fg(Color::White));

        let pitcher_rows: Vec<Row> = pitchers
            .iter()
            .map(|r| {
                Row::new(vec![
                    Cell::from(r.player.name.clone()),
                    Cell::from(r.player.hand.clone().unwrap_or_default()),
                    Cell::from(r.player.positions().join("/")),
                    Cell::from(format_rating(r.rating_vl)),
                    Cell::from(format_rating(r.rating_vr)),
                    Cell::from(format_rating(r.rating_overall)),
                    Cell::from(format_swar(r.player.swar)),
                    Cell::from(format_endurance(r.endurance_start)),
                    Cell::from(format_endurance(r.endurance_relief)),
                    Cell::from(format_endurance(r.endurance_close)),
                ])
            })
            .collect();

        let pitcher_table = Table::new(
            pitcher_rows,
            [
                Constraint::Length(20), // Name
                Constraint::Length(3),  // Hand
                Constraint::Length(14), // Pos
                Constraint::Length(6),  // vL
                Constraint::Length(6),  // vR
                Constraint::Length(6),  // Ovr
                Constraint::Length(6),  // sWAR
                Constraint::Length(3),  // S
                Constraint::Length(3),  // R
                Constraint::Length(3),  // C
            ],
        )
        .header(pitcher_header)
        .row_highlight_style(Style::default().bg(Color::DarkGray));

        let mut pitcher_state = TableState::default();
        pitcher_state.select(pitcher_selected);
        frame.render_stateful_widget(pitcher_table, table_chunks[3], &mut pitcher_state);
    }

    fn render_hints(&self, frame: &mut Frame, area: Rect) {
        let hints = Paragraph::new(" [1-3] Tab  [j/k] Scroll  [f] Refresh")
            .style(Style::default().fg(Color::DarkGray));
        frame.render_widget(hints, area);
    }
}

// =============================================================================
// Helpers
// =============================================================================

async fn build_roster_rows(pool: &SqlitePool, players: &[Player]) -> Vec<RosterRow> {
    let mut rows = Vec::with_capacity(players.len());
    for player in players {
        let (rating_vl, rating_vr, rating_overall, end_s, end_r, end_c) = if player.is_pitcher() {
            match crate::db::queries::get_pitcher_card(pool, player.id).await {
                Ok(Some(card)) => (
                    card.rating_vlhb,
                    card.rating_vrhb,
                    card.rating_overall,
                    card.endurance_start,
                    card.endurance_relief,
                    card.endurance_close,
                ),
                _ => (None, None, None, None, None, None),
            }
        } else {
            match crate::db::queries::get_batter_card(pool, player.id).await {
                Ok(Some(card)) => (card.rating_vl, card.rating_vr, card.rating_overall, None, None, None),
                _ => (None, None, None, None, None, None),
            }
        };

        rows.push(RosterRow {
            player: player.clone(),
            rating_vl,
            rating_vr,
            rating_overall,
            endurance_start: end_s,
            endurance_relief: end_r,
            endurance_close: end_c,
        });
    }
    rows
}

fn format_endurance(val: Option<i64>) -> String {
    match val {
        Some(v) => v.to_string(),
        None => "—".to_string(),
    }
}
