use crate::data_provider::voice::get_voice;
use crate::lang::Id;
use crate::lang::{CardStatistics, DeckData, DeckSettings, WordData, WordOpenMode};
use crate::navigation::Page::PreviousPage;
use crate::navigation::{KeyPressedPage, NavigatedPage, Page};
use crate::styling::*;
use crate::{AppState, RootMessage};
use chrono::Local;
use iced::alignment::Horizontal::Center;
use iced::keyboard::key::Physical::Code;
use iced::widget::text::Style;
use iced::widget::{Column, button, column, container, row, rule, space, text, tooltip};
use iced::{Element, Fill, Task, Theme, alignment, keyboard};
use rodio::MixerDeviceSink;
use std::collections::HashSet;
use std::sync::Arc;
use parking_lot::Mutex;
use tokio::task::spawn_blocking;

pub struct RepetitionState {
    state: Arc<Mutex<AppState>>,
    pub settings: DeckSettings,
    pub set: DeckData,
    current_word: WordData,
    current_statistic: CardStatistics,
    open: bool,
    can_play: bool,
    sink: Arc<MixerDeviceSink>,
    opened: HashSet<Id>,
}

impl NavigatedPage<RepetitionMessage> for RepetitionState {
    fn navigate(&mut self, message: &RepetitionMessage) -> Option<Page> {
        if let RepetitionMessage::Back = message {
            Some(PreviousPage)
        } else {
            None
        }
    }

    fn navigated(&mut self) {}
    fn update(&mut self, message: RepetitionMessage) -> Task<RootMessage> {
        match message {
            RepetitionMessage::Back => {}
            RepetitionMessage::Next => return self.next(),
            RepetitionMessage::Answer(m) => return self.answer(m),
            RepetitionMessage::Play => {
                if !self.can_play {
                    return Task::none();
                }

                self.can_play = false;
                let value = self.current_word.key.clone();
                if self.settings.require_speech() {
                    return Task::perform(play_sound(self.sink.clone(), value), |_| {
                        RootMessage::Repetition(RepetitionMessage::PlayFinished)
                    });
                }
            }
            RepetitionMessage::PlayFinished => {
                self.can_play = true;
            }
        }

        Task::none()
    }
    fn view(&self) -> Element<'_, RepetitionMessage> {
        back_overlay(
            column![
                container(self.draw_forward())
                    .width(Fill)
                    .height(Fill)
                    .align_x(Center)
                    .align_y(alignment::Vertical::Center),
                rule::horizontal(2),
                container(self.draw_backward())
                    .width(Fill)
                    .height(Fill)
                    .align_x(Center)
                    .align_y(alignment::Vertical::Center),
                container(self.answer_bar())
                    .width(Fill)
                    .align_x(Center)
                    .height(80),
                text!(
                    "Затронуто слов {}, {}%",
                    self.opened.len(),
                    (self.opened.len() as f32 / self.set.len() as f32 * 10000.0).round() / 100.0
                )
            ]
            .height(Fill)
            .width(Fill)
            .into(),
            RepetitionMessage::Back,
        )
    }
}

impl RepetitionState {
    pub(crate) fn new(set: DeckSettings, state: Arc<Mutex<AppState>>) -> RepetitionState {
        let mut card_set = DeckData::new(&set, state.clone());
        let (word, stat) = card_set.next();
        let sink_handle = rodio::DeviceSinkBuilder::open_default_sink().unwrap();

        RepetitionState {
            state,
            settings: set,
            set: card_set,
            current_word: word,
            current_statistic: stat,
            open: false,
            can_play: true,
            sink: Arc::new(sink_handle),
            opened: HashSet::new(),
        }
    }
}

impl RepetitionState {
    fn next(&mut self) -> Task<RootMessage> {
        if self.open {
            self.answer(WordOpenMode::None)
        } else {
            self.open = true;
            Task::none()
        }
    }

    fn answer(&mut self, mode: WordOpenMode) -> Task<RootMessage> {
        if !self.open {
            return Task::none();
        }

        self.set.open(mode);
        self.open = false;
        self.opened.insert(self.current_word.id);
        let next = self.set.next();
        self.current_word = next.0;
        self.current_statistic = next.1;

        if self.settings.require_speech() {
            return Task::perform(
                play_sound(self.sink.clone(), self.current_word.key.clone()),
                |_| RootMessage::Repetition(RepetitionMessage::PlayFinished),
            );
        }
        self.increment_counter();
        Task::none()
    }

    fn increment_counter(&mut self) {
        let mut state = self.state.lock();
        let id = self.settings.id;
        let activity = state.activity.get_mut(&id);

        let now = Local::now().date_naive();

        if let Some(activity) = activity {
            let (last_day, counter) = activity.last_mut().unwrap();
            if *last_day == now {
                *counter += 1;
            } else {
                activity.push((now, 1));
            }
        } else {
            state.activity.insert(id, vec![(now, 1)]);
        }
    }

    fn draw_forward(&self) -> Element<'_, RepetitionMessage> {
        self.draw_card_view(self.settings.forward.as_str())
    }

    fn draw_backward(&self) -> Element<'_, RepetitionMessage> {
        if !self.open {
            return container(text!("?").size(72))
                .width(Fill)
                .height(Fill)
                .align_y(alignment::Vertical::Center)
                .align_x(Center)
                .into();
        }

        self.draw_card_view(self.settings.backward.as_str())
    }

    fn draw_card_view(&self, properties: &str) -> Element<'_, RepetitionMessage> {
        let word = &self.current_word;

        let mut col = Column::new();

        for view_type in properties.split(" ") {
            col = col.push(match view_type {
                "key" => self.draw_key(word),
                "value" => self.draw_value(word),
                "speech" => self.draw_voice(),
                "reading" => self.draw_reading(word),
                "context" => self.draw_context(word),
                "description" => self.draw_description(word),
                _ => space().into(),
            })
        }

        col.spacing(DEFAULT_SPACING).align_x(Center).into()
    }

    fn answer_bar(&self) -> Element<'_, RepetitionMessage> {
        if !self.open {
            return text!("Нажмите Пробел для открытия карточки")
                .style(|theme: &Theme| Style {
                    color: Some(theme.palette().text.scale_alpha(0.3)),
                })
                .into();
        }
        column![
            text!(
                "{} очков",
                self.current_statistic.calculated_score().round() as i32
            ),
            row![
                tooltip(
                    button("Не получилось").on_press(RepetitionMessage::Answer(WordOpenMode::None)),
                    "Клавиша 1",
                    tooltip::Position::Top
                ),
                tooltip(
                    button("Трудно").on_press(RepetitionMessage::Answer(WordOpenMode::Hard)),
                    "Клавиша 2",
                    tooltip::Position::Top
                ),
                tooltip(
                    button("Нормально").on_press(RepetitionMessage::Answer(WordOpenMode::Ok)),
                    "Клавиша 3",
                    tooltip::Position::Top
                ),
                tooltip(
                    button("Легко").on_press(RepetitionMessage::Answer(WordOpenMode::Easy)),
                    "Клавиша 4",
                    tooltip::Position::Top
                ),
            ]
            .spacing(QUARTER_SPACING)
        ]
        .align_x(Center)
        .spacing(DEFAULT_SPACING)
        .into()
    }

    fn draw_key(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
        text!("{}", word.key).size(36).into()
    }
    fn draw_value(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
        text!("{}", word.value).size(24).into()
    }

    fn draw_voice(&self) -> Element<'_, RepetitionMessage> {
        button("Воспроизвести")
            .style(jl_button)
            .on_press(RepetitionMessage::Play)
            .into()
    }

    fn draw_reading(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
        match word.additional.get("reading") {
            None => space().into(),
            Some(reading) => text!("{}", reading).size(24).into(),
        }
    }
    fn draw_context(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
        match word.additional.get("context") {
            None => space().into(),
            Some(context) => text!("{}", context).size(24).into(),
        }
    }
    fn draw_description(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
        match word.additional.get("context") {
            None => space().into(),
            Some(context) => text!("{}", context).size(24).into(),
        }
    }
}

impl KeyPressedPage for RepetitionState {
    fn press(&mut self, message: &keyboard::Event) -> Task<RootMessage> {
        if let keyboard::Event::KeyPressed {
            key: _,
            modified_key: _,
            physical_key: pk,
            location: _,
            modifiers: _,
            text: _,
            repeat: _,
        } = message
            && let Code(code) = pk
        {
            return match code {
                keyboard::key::Code::Space => self.next(),
                keyboard::key::Code::Digit1 => self.answer(WordOpenMode::None),
                keyboard::key::Code::Digit2 => self.answer(WordOpenMode::Hard),
                keyboard::key::Code::Digit3 => self.answer(WordOpenMode::Ok),
                keyboard::key::Code::Digit4 => self.answer(WordOpenMode::Easy),
                _ => Task::none(),
            };
        }

        Task::none()
    }
}

#[derive(Clone)]
pub enum RepetitionMessage {
    Next,
    Back,
    Answer(WordOpenMode),
    Play,
    PlayFinished,
}

async fn play_sound(sink: Arc<MixerDeviceSink>, text: String) {
    let data = get_voice(text.as_str()).await;
    spawn_blocking(move || {
        rodio::play(sink.mixer(), data).unwrap().sleep_until_end();
    })
    .await
    .unwrap();
}
