use std::sync::{Arc, RwLock};

use crate::HarmonyTuiEvent;
use crossterm::event::{Event, KeyCode, KeyEventKind};
use harmony::{score::Score, topology::Topology};
use log::{info, warn};
use ratatui::{
    Frame,
    layout::Rect,
    style::{Style, Stylize},
    widgets::{List, ListItem, ListState, StatefulWidget, Widget},
};
use tokio::sync::mpsc;

#[derive(Debug)]
enum ExecutionState {
    INITIATED,
    RUNNING,
    CANCELED,
}

struct Execution<T: Topology> {
    state: ExecutionState,
    score: Box<dyn Score<T>>,
}

impl<T: Topology> std::fmt::Display for Execution<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!(
            "Execution of {} status {:?}",
            self.score.name(),
            self.state
        ))
    }
}

pub(crate) struct ScoreListWidget<T: Topology> {
    list_state: Arc<RwLock<ListState>>,
    scores: Vec<Box<dyn Score<T>>>,
    execution: Option<Execution<T>>,
    execution_history: Vec<Execution<T>>,
    sender: mpsc::Sender<HarmonyTuiEvent<T>>,
}

impl<T: Topology> ScoreListWidget<T> {
    pub(crate) fn new(
        scores: Vec<Box<dyn Score<T>>>,
        sender: mpsc::Sender<HarmonyTuiEvent<T>>,
    ) -> Self {
        let mut list_state = ListState::default();
        list_state.select_first();
        let list_state = Arc::new(RwLock::new(list_state));
        Self {
            scores,
            list_state,
            execution: None,
            execution_history: vec![],
            sender,
        }
    }

    pub(crate) fn launch_execution(&mut self) {
        if let Some(score) = self.get_selected_score() {
            self.execution = Some(Execution {
                state: ExecutionState::INITIATED,
                score: score.clone_box(),
            });
            info!("{}\n\nConfirm Execution (Press y/n)", score.name());
            info!("{}", score.print_score_details());
        } else {
            warn!("No Score selected, nothing to launch");
        }
    }

    pub(crate) fn get_selected_score(&self) -> Option<Box<dyn Score<T>>> {
        let list_read = self.list_state.read().unwrap();
        if let Some(index) = list_read.selected() {
            self.scores.get(index).map(|s| s.clone_box())
        } else {
            None
        }
    }

    pub(crate) fn scroll_down(&self) {
        self.list_state.write().unwrap().scroll_down_by(1);
    }

    pub(crate) fn scroll_up(&self) {
        self.list_state.write().unwrap().scroll_up_by(1);
    }

    pub(crate) fn render(&self, area: Rect, frame: &mut Frame) {
        frame.render_widget(self, area);
    }

    fn clear_execution(&mut self) {
        match self.execution.take() {
            Some(execution) => {
                self.execution_history.push(execution);
            }
            None => warn!("Should never clear execution when no execution exists"),
        }
    }

    pub(crate) async fn confirm(&mut self, confirm: bool) {
        if let Some(execution) = &mut self.execution {
            match confirm {
                true => {
                    execution.state = ExecutionState::RUNNING;
                    info!("Launch execution {execution}");
                    self.sender
                        .send(HarmonyTuiEvent::LaunchScore(execution.score.clone_box()))
                        .await
                        .expect("Should be able to send message");
                }
                false => {
                    execution.state = ExecutionState::CANCELED;
                    info!("Execution cancelled");
                    self.clear_execution();
                }
            }
        }
    }

    pub(crate) async fn handle_event(&mut self, event: &Event) {
        if let Event::Key(key) = event {
            if key.kind == KeyEventKind::Press {
                match key.code {
                    KeyCode::Char('j') | KeyCode::Down => self.scroll_down(),
                    KeyCode::Char('k') | KeyCode::Up => self.scroll_up(),
                    KeyCode::Enter => self.launch_execution(),
                    KeyCode::Char('y') => self.confirm(true).await,
                    KeyCode::Char('n') => self.confirm(false).await,
                    _ => {}
                }
            }
        }
    }
}

impl<T: Topology> Widget for &ScoreListWidget<T> {
    fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
    where
        Self: Sized,
    {
        let mut list_state = self.list_state.write().unwrap();
        let scores_items: Vec<ListItem<'_>> = self.scores.iter().map(score_to_list_item).collect();
        let list = List::new(scores_items)
            .highlight_style(Style::new().bold().italic())
            .highlight_symbol("🠊 ");

        StatefulWidget::render(list, area, buf, &mut list_state)
    }
}

fn score_to_list_item<'a, T: Topology>(score: &'a Box<dyn Score<T>>) -> ListItem<'a> {
    ListItem::new(score.name())
}
