use super::tournament_brackets_lines::{current_round, number_of_rounds};
use super::traits::{HelpContent, HelpPanel, Screen, SplitPanel};
use crate::core::{skill::Rated, world::World};
use crate::game_engine::game::GameSummary;
use crate::game_engine::{Tournament, TournamentId, TournamentState, TournamentSummary};
use crate::types::{AppResult, SystemTimeTick, Tick};
use crate::ui::button::Button;
use crate::ui::clickable_list::ClickableListState;
use crate::ui::panels::tournament_brackets_lines;
use crate::ui::ui_callback::UiCallback;
use crate::ui::ui_frame::UiFrame;
use crate::ui::ui_key;
use crate::ui::ui_screen::{tab_link, UiTab};
use crate::ui::{
    constants::*,
    renders::{default_block, selectable_list},
};
use core::fmt::Debug;
use itertools::Itertools;
use ratatui::crossterm;
use ratatui::crossterm::event::KeyCode;
use ratatui::layout::Margin;
use ratatui::style::Stylize;
use ratatui::text::Line;
use ratatui::{
    layout::{Constraint, Layout},
    prelude::Rect,
    widgets::Paragraph,
};
use std::fmt::Display;

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub enum TournamentView {
    #[default]
    All,
    Open,
    Past,
}

impl TournamentView {
    const fn next(&self) -> Self {
        match self {
            Self::All => Self::Open,
            Self::Open => Self::Past,
            Self::Past => Self::All,
        }
    }

    const fn previous(&self) -> Self {
        match self {
            Self::All => Self::Past,
            Self::Open => Self::All,
            Self::Past => Self::Open,
        }
    }

    fn rule(&self, tournament_id: &TournamentId, world: &World) -> bool {
        match self {
            Self::All => true,
            Self::Open => world
                .tournaments
                .get(tournament_id)
                .map(|t| !t.has_started(Tick::now()))
                .unwrap_or_default(),
            Self::Past => world.past_tournaments.get(tournament_id).is_some(),
        }
    }
}

impl Display for TournamentView {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::All => write!(f, "All"),
            Self::Open => write!(f, "Open to registration"),
            Self::Past => write!(f, "Past"),
        }
    }
}

#[derive(Debug, Default)]
pub struct TournamentPanel {
    index: Option<usize>,
    selected_tournament_id: TournamentId,
    filtered_tournament_ids: Vec<TournamentId>,
    past_tournament_ids: Vec<TournamentId>,
    all_tournament_ids: Vec<TournamentId>,
    view: TournamentView,
    update_view: bool,
    tick: usize,
    tournament_list_state: ClickableListState,
    tournament_summary_list_state: ClickableListState,
}

impl TournamentPanel {
    pub fn new() -> Self {
        Self::default()
    }

    fn build_left_panel(&mut self, frame: &mut UiFrame, world: &World, area: Rect) {
        let split = Layout::vertical([
            Constraint::Length(3),
            Constraint::Length(3),
            Constraint::Length(3),
            Constraint::Fill(1),
        ])
        .split(area);

        let mut filter_all_button = Button::new(
            TournamentView::All.to_string(),
            UiCallback::SetTournamentPanelView {
                view: TournamentView::All,
            },
        )
        .bold()
        .hover_text("View all tournaments.");

        let mut filter_open_button = Button::new(
            TournamentView::Open.to_string(),
            UiCallback::SetTournamentPanelView {
                view: TournamentView::Open,
            },
        )
        .bold()
        .hover_text("View all tournaments yet to start.");

        let mut filter_past_button = Button::new(
            TournamentView::Past.to_string(),
            UiCallback::SetTournamentPanelView {
                view: TournamentView::Past,
            },
        )
        .bold()
        .hover_text("View all past tournaments.");

        match self.view {
            TournamentView::All => filter_all_button.select(),
            TournamentView::Open => filter_open_button.select(),
            TournamentView::Past => filter_past_button.select(),
        }

        frame.render_interactive_widget(filter_all_button, split[0]);
        frame.render_interactive_widget(filter_open_button, split[1]);
        frame.render_interactive_widget(filter_past_button, split[2]);

        frame.render_widget(default_block().title("Tournaments ↓/↑"), split[3]);

        if self.view == TournamentView::Past {
            self.build_tournament_summary_list(frame, world, split[3].inner(Margin::new(1, 1)));
        } else {
            self.build_tournament_list(frame, world, split[3].inner(Margin::new(1, 1)));
        }
    }

    fn build_tournament_list(&mut self, frame: &mut UiFrame, world: &World, area: Rect) {
        if !self.filtered_tournament_ids.is_empty() {
            let mut options = vec![];
            for tournament_id in self.filtered_tournament_ids.iter() {
                let tournament = if let Some(t) = world.tournaments.get(tournament_id) {
                    t
                } else {
                    continue;
                };
                let style = if tournament.organizer_id == world.own_team_id {
                    UiStyle::OWN_TEAM
                } else {
                    UiStyle::DEFAULT
                };

                let text = format!("{:<24} {}", tournament.name(), tournament.stars());
                options.push((text, style));
            }
            let list = selectable_list(options);

            self.tournament_list_state.select(self.index);
            frame.render_stateful_interactive_widget(list, area, &mut self.tournament_list_state);
        }
    }

    fn build_tournament_summary_list(&mut self, frame: &mut UiFrame, world: &World, area: Rect) {
        if !self.past_tournament_ids.is_empty() {
            let mut options = vec![];
            for tournament_id in self.filtered_tournament_ids.iter() {
                let tournament = if let Some(t) = world.past_tournaments.get(tournament_id) {
                    t
                } else {
                    continue;
                };
                let style = if tournament.organizer_id == world.own_team_id {
                    UiStyle::OWN_TEAM
                } else {
                    UiStyle::DEFAULT
                };

                let text = format!("{:<24} {}", tournament.name(), tournament.stars());
                options.push((text, style));
            }
            let list = selectable_list(options);

            self.tournament_summary_list_state.select(self.index);
            frame.render_stateful_interactive_widget(
                list,
                area,
                &mut self.tournament_summary_list_state,
            );
        }
    }

    fn build_right_panel(&self, frame: &mut UiFrame, world: &World, area: Rect) -> AppResult<()> {
        let index = if let Some(index) = self.index {
            index
        } else {
            frame.render_widget(default_block(), area);
            return Ok(());
        };

        let tournament_id = self
            .filtered_tournament_ids
            .get(index)
            .expect("Tournament id selection should be valid.");

        let split = Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]).split(area);
        if self.view == TournamentView::Past {
            let tournament_summary = if let Some(t) = world.past_tournaments.get(tournament_id) {
                t
            } else {
                frame.render_widget(default_block(), area);
                return Ok(());
            };
            let planet_id = tournament_summary.planet_id;
            let tournament_main_button = Button::new(
                tournament_summary.name(),
                UiCallback::GoToPlanet { planet_id },
            );
            frame.render_interactive_widget(tournament_main_button, split[0]);
            self.render_past_tournament(tournament_summary, frame, world, split[1])?;
            return Ok(());
        }

        let tournament = if let Some(t) = world.tournaments.get(tournament_id) {
            t
        } else {
            frame.render_widget(default_block(), area);
            return Ok(());
        };

        let planet_id = tournament.planet_id;
        let tournament_main_button =
            Button::new(tournament.name(), UiCallback::GoToPlanet { planet_id });
        frame.render_interactive_widget(tournament_main_button, split[0]);

        match tournament.state(Tick::now()) {
            TournamentState::Canceled => {}
            TournamentState::Registration => {
                self.render_registration_tournament(tournament, frame, world, split[1])?
            }
            TournamentState::Confirmation => {
                self.render_confirmation_tournament(tournament, frame, split[1])?
            }
            TournamentState::Syncing => {
                self.render_syncing_tournament(tournament, frame, split[1])?
            }
            TournamentState::Started => {
                if tournament.is_initialized() {
                    self.render_started_tournament(tournament, frame, world, split[1])?
                }
            }
            TournamentState::Ended => {}
        }

        Ok(())
    }

    fn render_registration_tournament(
        &self,
        tournament: &Tournament,
        frame: &mut UiFrame,
        world: &World,
        area: Rect,
    ) -> AppResult<()> {
        let t_split = Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]).split(area);
        let countdown = (tournament
            .registrations_closing_at
            .saturating_sub(Tick::now()))
        .formatted();
        frame.render_widget(
            Paragraph::new(format!(
                "Registrations closing in {} - Max participants {}",
                countdown, tournament.max_participants
            ))
            .centered()
            .block(default_block()),
            t_split[0],
        );

        let split = Layout::horizontal([Constraint::Length(LEFT_PANEL_WIDTH), Constraint::Fill(1)])
            .split(t_split[1]);

        let options = tournament
            .registered_teams
            .values()
            .sorted_by(|a, b| a.team_id.cmp(&b.team_id))
            .map(|team| {
                let mut style = UiStyle::DEFAULT;
                if team.team_id == world.own_team_id {
                    style = UiStyle::OWN_TEAM;
                } else if team.peer_id.is_some() {
                    style = UiStyle::NETWORK;
                }
                let text = format!("{:<MAX_NAME_LENGTH$} {}", team.name, team.rating().stars());
                (text, style)
            })
            .collect_vec();

        let list = selectable_list(options);

        frame.render_stateful_interactive_widget(
            list.block(default_block().title("Registered crews ↓/↑")),
            split[0],
            &mut ClickableListState::default().with_selected(None),
        );

        let own_team = world.get_own_team()?;
        let b_split =
            Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]).split(split[1]);
        let mut register_button = Button::new(
            "Register now!",
            UiCallback::RegisterToTournament {
                tournament_id: tournament.id,
            },
        )
        .hotkey(ui_key::REGISTER_TO_TOURNAMENT)
        .hover_text(format!(
            "Register to {}. Participation will be confirmed on {} at {}.",
            tournament.name(),
            tournament.registrations_closing_at.formatted_as_date(),
            tournament.registrations_closing_at.formatted_as_time(),
        ));

        if let Err(err) = own_team.can_register_to_tournament(tournament, Tick::now()) {
            register_button.disable(Some(err.to_string()));
            if tournament.is_team_registered(&world.own_team_id) {
                register_button = register_button.set_text("Already registered");
            }
        }

        frame.render_interactive_widget(register_button, b_split[0]);

        if tournament.organizer_id == world.own_team_id {
            frame.render_widget(
                Paragraph::new(vec![
                    Line::from("Thanks for organizing!"),
                    Line::from("Be sure to be online when registrations close"),
                    Line::from("or the tournament will be canceled."),
                ])
                .centered(),
                b_split[1],
            );
        } else {
            frame.render_widget(
                Paragraph::new(vec![
                Line::from("Register at any time and be sure to be online at the time registrations close"),
                Line::from("to be able to confirm your participation."),
                Line::from(format!("{} participants will be chosen at random from all the registered crews", tournament.max_participants)),
                Line::from("that confirmed their participation.")]).centered(),
                b_split[1],
            );
        }

        Ok(())
    }

    fn render_confirmation_tournament(
        &self,
        tournament: &Tournament,
        frame: &mut UiFrame,
        area: Rect,
    ) -> AppResult<()> {
        let t_split = Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]).split(area);
        frame.render_widget(
            Paragraph::new("Selecting participants...")
                .centered()
                .block(default_block()),
            t_split[0],
        );

        let split = Layout::horizontal([Constraint::Length(LEFT_PANEL_WIDTH), Constraint::Fill(1)])
            .split(t_split[1]);

        let options = tournament
            .registered_teams
            .values()
            .sorted_by(|a, b| a.team_id.cmp(&b.team_id))
            .map(|team| {
                let style = if tournament.participants.contains_key(&team.team_id) {
                    UiStyle::OK
                } else {
                    UiStyle::WARNING
                };
                let text = format!("{:<MAX_NAME_LENGTH$} {}", team.name, team.rating().stars());
                (text, style)
            })
            .collect_vec();

        let list = selectable_list(options);

        frame.render_stateful_interactive_widget(
            list.block(default_block().title("Registered crews ↓/↑")),
            split[0],
            &mut ClickableListState::default().with_selected(None),
        );
        Ok(())
    }

    fn render_syncing_tournament(
        &self,
        tournament: &Tournament,
        frame: &mut UiFrame,
        area: Rect,
    ) -> AppResult<()> {
        let t_split = Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]).split(area);
        frame.render_widget(
            Paragraph::new("Syncing...")
                .centered()
                .block(default_block()),
            t_split[0],
        );

        let split = Layout::horizontal([Constraint::Length(LEFT_PANEL_WIDTH), Constraint::Fill(1)])
            .split(t_split[1]);

        let options = tournament
            .registered_teams
            .values()
            .sorted_by(|a, b| a.team_id.cmp(&b.team_id))
            .map(|team| {
                let style = if tournament.participants.contains_key(&team.team_id) {
                    UiStyle::OK
                } else {
                    UiStyle::ERROR
                };
                let text = format!("{:<MAX_NAME_LENGTH$} {}", team.name, team.rating().stars());
                (text, style)
            })
            .collect_vec();

        let list = selectable_list(options);

        frame.render_stateful_interactive_widget(
            list.block(default_block().title("Registered crews ↓/↑")),
            split[0],
            &mut ClickableListState::default().with_selected(None),
        );
        Ok(())
    }

    fn render_started_tournament(
        &self,
        tournament: &Tournament,
        frame: &mut UiFrame,
        world: &World,
        area: Rect,
    ) -> AppResult<()> {
        let t_split = Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]).split(area);

        let active_games = tournament.active_games(&world.games);
        let past_game_summaries = tournament.past_game_summaries(&world.past_games);

        let num_participants = tournament.participants.len();
        let number_of_rounds = number_of_rounds(num_participants);
        let current_round = current_round(num_participants, past_game_summaries.len()) + 1;

        frame.render_widget(
            Paragraph::new(format!(
                "Currently playing round {current_round}/{number_of_rounds}"
            ))
            .centered()
            .block(default_block()),
            t_split[0],
        );

        let brackets_split =
            Layout::horizontal([Constraint::Length(24)].repeat(number_of_rounds + 1))
                .split(t_split[1]);

        let brackets = tournament_brackets_lines::get_bracket_lines(
            tournament.winner.map(|id| {
                tournament
                    .participants
                    .get(&id)
                    .expect("Winner should be a participant")
                    .name
                    .as_str()
            }),
            num_participants,
            &active_games,
            &past_game_summaries,
            world.own_team_id,
            Tick::now(),
        );
        for (round_idx, lines) in brackets.iter().enumerate() {
            frame.render_widget(Paragraph::new(lines.clone()), brackets_split[round_idx]);
        }

        Ok(())
    }

    fn render_past_tournament(
        &self,
        tournament_summary: &TournamentSummary,
        frame: &mut UiFrame,
        world: &World,
        area: Rect,
    ) -> AppResult<()> {
        let t_split = Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]).split(area);
        let ended_at = tournament_summary
            .ended_at
            .expect("Tournament should be ended.");
        frame.render_widget(
            Paragraph::new(format!(
                "Ended on {} at {}",
                ended_at.formatted_as_date(),
                ended_at.formatted_as_time()
            ))
            .centered()
            .block(default_block()),
            t_split[0],
        );

        let games = vec![];
        let game_summaries = tournament_summary
            .game_ids
            .iter()
            .filter_map(|id| world.past_games.get(id))
            .collect::<Vec<&GameSummary>>();

        let num_participants = tournament_summary.participant_ids.len();

        log::info!(
            "render_past_tournament id={} participants={num_participants} game_summaries={}",
            tournament_summary.id,
            game_summaries.len()
        );

        let brackets = tournament_brackets_lines::get_bracket_lines(
            Some(tournament_summary.winner_name.as_str()),
            num_participants,
            &games,
            &game_summaries,
            world.own_team_id,
            Tick::now(),
        );

        let brackets_split =
            Layout::horizontal([Constraint::Length(24)].repeat(brackets.len())).split(t_split[1]);
        for (round_idx, lines) in brackets.iter().enumerate() {
            frame.render_widget(Paragraph::new(lines.clone()), brackets_split[round_idx]);
        }

        Ok(())
    }

    pub const fn set_view(&mut self, filter: TournamentView) {
        self.view = filter;
        self.update_view = true;
        self.index = None;
        self.tournament_list_state.reset_offset();
    }

    pub const fn reset_view(&mut self) {
        self.set_view(TournamentView::All);
    }

    pub fn set_active_tournament(
        &mut self,
        tournament_id: TournamentId,
        world: &World,
    ) -> AppResult<()> {
        self.set_view(if world.past_tournaments.contains_key(&tournament_id) {
            TournamentView::Past
        } else {
            TournamentView::All
        });
        self.update(world)?;
        if let Some(index) = self
            .filtered_tournament_ids
            .iter()
            .position(|&id| id == tournament_id)
        {
            self.set_index(index);
            self.update(world)?;
        }
        Ok(())
    }
}

impl Screen for TournamentPanel {
    fn tick(&mut self) {
        self.tick += 1;
    }

    fn update(&mut self, world: &World) -> AppResult<()> {
        if world.dirty_ui || self.all_tournament_ids.len() != world.tournaments.len() {
            self.all_tournament_ids = world
                .tournaments
                .values()
                .sorted_by(|a, b| a.starting_at().cmp(&b.starting_at()))
                .map(|t| &t.id)
                .copied()
                .collect();
            self.past_tournament_ids = world
                .past_tournaments
                .values()
                .sorted_by(|a, b| a.starting_at().cmp(&b.starting_at()))
                .map(|t| &t.id)
                .copied()
                .collect();

            self.update_view = true;
        }

        if self.update_view {
            self.filtered_tournament_ids = if self.view == TournamentView::Past {
                self.past_tournament_ids.to_vec()
            } else {
                self.all_tournament_ids
                    .iter()
                    .filter(|&id| self.view.rule(id, world))
                    .copied()
                    .collect()
            };

            self.update_view = false;
        }

        if let Some(index) = self.index {
            if self.filtered_tournament_ids.is_empty() {
                self.index = None;
            } else if index >= self.filtered_tournament_ids.len()
                && !self.filtered_tournament_ids.is_empty()
            {
                self.set_index(self.filtered_tournament_ids.len() - 1);
            }

            if index < self.filtered_tournament_ids.len() {
                self.selected_tournament_id = self.filtered_tournament_ids[index];
            }
        } else if !self.filtered_tournament_ids.is_empty() {
            self.set_index(0);
        }

        Ok(())
    }
    fn render(
        &mut self,
        frame: &mut UiFrame,
        world: &World,
        area: Rect,

        _debug_view: bool,
    ) -> AppResult<()> {
        // Split into left and right panels
        let left_right_split =
            Layout::horizontal([Constraint::Length(LEFT_PANEL_WIDTH), Constraint::Fill(1)])
                .split(area);
        self.build_left_panel(frame, world, left_right_split[0]);

        if self.filtered_tournament_ids.is_empty() {
            frame.render_widget(
                default_block().title(" No tournaments at the moment..."),
                left_right_split[1],
            );
            return Ok(());
        }
        self.build_right_panel(frame, world, left_right_split[1])?;
        Ok(())
    }

    fn handle_key_events(
        &mut self,
        key_event: crossterm::event::KeyEvent,
        _world: &World,
    ) -> Option<UiCallback> {
        match key_event.code {
            KeyCode::Up => self.next_index(),
            KeyCode::Down => self.previous_index(),
            ui_key::CYCLE_VIEW => {
                return Some(UiCallback::SetTournamentPanelView {
                    view: self.view.next(),
                });
            }
            ui_key::CYCLE_VIEW_BACK => {
                return Some(UiCallback::SetTournamentPanelView {
                    view: self.view.previous(),
                });
            }

            _ => {}
        }
        None
    }

    fn footer_spans(&self) -> Vec<String> {
        vec![
            format!(" {} ", ui_key::CYCLE_VIEW.to_string()),
            " Next tab ".to_string(),
            format!(
                " {}/{} ",
                ui_key::PREVIOUS_SELECTION.to_string(),
                ui_key::NEXT_SELECTION.to_string()
            ),
            " Select player ".to_string(),
        ]
    }
}

impl HelpPanel for TournamentPanel {
    fn help_content(&self) -> HelpContent {
        HelpContent {
            description: [
                " Track running tournaments and register your team or browse past tournaments.",
                "",
                "Watch live or finished tournament games in Games.",
                "Tune your roster before registering in My Team.",
                "Scout potential opponents in Crews.",
            ]
            .join("\n"),
            links: vec![
                tab_link("Games", UiTab::Games),
                tab_link("My Team", UiTab::MyTeam),
                tab_link("Crews", UiTab::Crews),
            ],
            controls: vec![
                Line::from(" Controls:"),
                Line::from(format!(
                    "  {}        Cycle view (All / Active / Past)",
                    ui_key::CYCLE_VIEW
                )),
                Line::from("  ↑/↓        Move highlight in tournament list"),
                Line::from(format!(
                    "  {} / {}      Scroll the inner roster preview",
                    ui_key::PREVIOUS_SELECTION,
                    ui_key::NEXT_SELECTION
                )),
                Line::from(format!(
                    "  {}          Register your team for the highlighted tournament",
                    ui_key::REGISTER_TO_TOURNAMENT
                )),
                Line::from(format!(
                    "  {} / {}      Organize a quick / big tournament",
                    ui_key::ORGANIZE_QUICK_TOURNAMENT,
                    ui_key::ORGANIZE_BIG_TOURNAMENT
                )),
            ],
        }
    }
}

impl SplitPanel for TournamentPanel {
    fn index(&self) -> Option<usize> {
        self.index
    }

    fn max_index(&self) -> usize {
        self.filtered_tournament_ids.len()
    }

    fn set_index(&mut self, index: usize) {
        if self.max_index() == 0 {
            self.index = None;
        } else {
            self.index = Some(index % self.max_index());
        }
    }
}
