use gtk4 as gtk;

use chrono::{Weekday, Weekday::*};
use gtk::prelude::*;
use std::collections::HashSet;

use crate::model::{
    repetition,
    repetition::{DayOfMonth, Frequency, Repetition},
};

static WEEKDAYS_STR: [&str; 7] = [
    "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche",
];

static WEEKDAYS: [Weekday; 7] = [Mon, Tue, Wed, Thu, Fri, Sat, Sun];

pub struct Model {
    pub view: gtk::Box,
    no_radio: gtk::CheckButton,
    day_interval_radio: gtk::CheckButton,
    day_interval_entry: gtk::Entry,
    monthly_radio: gtk::CheckButton,
    monthly_entry: gtk::Entry,
    first_day_radio: gtk::CheckButton,
    first_day_dropdown: gtk::DropDown,
    yearly_radio: gtk::CheckButton,
}

pub fn view(repetition: Option<&Repetition>) -> Model {
    let view = gtk::Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .build();
    view.add_css_class("g-Form__Inputs");

    view.append(&label("Répétition"));

    let no_radio = gtk::CheckButton::builder()
        .label("Non")
        .active(repetition.is_none())
        .build();
    view.append(&no_radio);

    let frequency = repetition.as_ref().map(|r| r.frequency.clone());

    let default = match frequency {
        Some(Frequency::Daily { period }) => period.to_string(),
        _ => "".to_string(),
    };
    let day_interval_entry = gtk::Entry::builder().text(&default).build();
    let (day_interval_box, day_interval_radio) = radio_input(
        &no_radio,
        !default.is_empty(),
        &day_interval_entry,
        "Interval de jours",
    );
    view.append(&day_interval_box);

    let default = match frequency {
        Some(Frequency::Monthly {
            day: DayOfMonth::Day { day },
        }) => day.to_string(),
        _ => "".to_string(),
    };
    let monthly_entry = gtk::Entry::builder().text(&default).build();
    let (monthly_box, monthly_radio) =
        radio_input(&no_radio, !default.is_empty(), &monthly_entry, "Mensuel");
    view.append(&monthly_box);

    let (active, default) = match frequency {
        Some(Frequency::Monthly {
            day: DayOfMonth::Weekday { weekday },
        }) => (true, weekday),
        _ => (false, Mon),
    };
    let first_day_dropdown = gtk::DropDown::from_strings(&WEEKDAYS_STR);
    first_day_dropdown
        .set_selected(WEEKDAYS.iter().position(|d| d == &default).unwrap_or(0) as u32);
    let (first_day_of_month_box, first_day_radio) =
        radio_input(&no_radio, active, &first_day_dropdown, "1er jour du mois");
    view.append(&first_day_of_month_box);

    let yearly_radio = gtk::CheckButton::builder()
        .group(&no_radio)
        .label("Annuel")
        .active(frequency == Some(Frequency::Yearly))
        .build();
    view.append(&yearly_radio);

    Model {
        view,
        no_radio,
        day_interval_radio,
        day_interval_entry,
        monthly_radio,
        monthly_entry,
        first_day_radio,
        first_day_dropdown,
        yearly_radio,
    }
}

fn radio_input(
    radio_group: &impl IsA<gtk::CheckButton>,
    active: bool,
    input: &impl IsA<gtk::Widget>,
    text: &str,
) -> (gtk::Box, gtk::CheckButton) {
    let radio_box = gtk::Box::builder().build();
    let radio = gtk::CheckButton::builder()
        .group(radio_group)
        .label(text)
        .active(active)
        .build();
    radio_box.append(&radio);
    input.add_css_class("g-Form__RadioInput");
    radio_box.append(input);
    (radio_box, radio)
}

fn label(text: &str) -> gtk::Label {
    gtk::Label::builder()
        .label(text)
        .halign(gtk::Align::Start)
        .margin_bottom(5)
        .build()
}

pub fn validate(model: &Model) -> Result<Option<Repetition>, String> {
    let frequency = if model.no_radio.is_active() {
        Ok(None)
    } else if model.day_interval_radio.is_active() {
        let period = repetition::validate_period(&model.day_interval_entry.buffer().text())?;
        Ok(Some(Frequency::Daily { period }))
    } else if model.monthly_radio.is_active() {
        let day = repetition::validate_day(&model.monthly_entry.buffer().text())?;
        Ok(Some(Frequency::Monthly {
            day: DayOfMonth::Day { day },
        }))
    } else if model.first_day_radio.is_active() {
        let weekday = WEEKDAYS[model.first_day_dropdown.selected() as usize];
        Ok(Some(Frequency::Monthly {
            day: DayOfMonth::Weekday { weekday },
        }))
    } else if model.yearly_radio.is_active() {
        Ok(Some(Frequency::Yearly))
    } else {
        Err("Aucune option n’a été sélectionnée".to_string())
    }?;

    Ok(frequency.map(|frequency| Repetition {
        frequency,
        removed_occurences: HashSet::new(),
    }))
}