use gtk4 as gtk;

use async_channel::Sender;
use chrono::{Datelike, NaiveDate};
use gtk::glib;
use gtk::prelude::*;
use std::collections::HashMap;

use crate::{gui::update, gui::update::Msg, gui::App, model::event, model::event::Event};

static DAYS: [&str; 7] = ["LUN", "MAR", "MER", "JEU", "VEN", "SAM", "DIM"];
static MONTHES: [&str; 12] = [
    "Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Aoû", "Sep", "Oct", "Nov", "Déc",
];

pub fn create(
    tx: Sender<Msg>,
    today: NaiveDate,
    start_date: NaiveDate,
    end_date: NaiveDate,
    events: &[Event],
    repeated_events: &[Event],
) -> gtk::Grid {
    let grid = gtk::Grid::builder().build();

    for col in 0..7 {
        grid.attach(&day_title(col), col, 0, 1, 1);
    }

    let repetitions = event::repetitions_between(repeated_events, start_date, end_date);
    attach_days(tx, &grid, start_date, today, events, &repetitions);

    grid
}

fn attach_days(
    tx: Sender<Msg>,
    grid: &gtk::Grid,
    start_date: NaiveDate,
    today: NaiveDate,
    events: &[Event],
    repetitions: &HashMap<NaiveDate, Vec<Event>>,
) {
    let mut d = start_date;
    for row in 1..5 {
        for col in 0..7 {
            grid.attach(
                &day_entry(tx.clone(), d, today, events, repetitions),
                col,
                row,
                1,
                1,
            );
            d = d.succ();
        }
    }
}

pub fn refresh_date(app: &App, date: NaiveDate, repetitions: &HashMap<NaiveDate, Vec<Event>>) {
    let d = date.signed_duration_since(app.start_date).num_days();

    let col = (d % 7) as i32;
    let row = 1 + (d / 7) as i32;

    app.grid.attach(
        &day_entry(app.tx.clone(), date, app.today, &app.events, repetitions),
        col,
        row,
        1,
        1,
    )
}

fn day_title(col: i32) -> gtk::Box {
    let vbox = gtk::Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .build();

    vbox.add_css_class("g-Calendar__DayTitle");

    let label = gtk::Label::builder().label(DAYS[col as usize]).build();

    vbox.append(&label);

    vbox
}

pub fn day_entry(
    tx: Sender<Msg>,
    date: NaiveDate,
    today: NaiveDate,
    events: &[Event],
    repetitions: &HashMap<NaiveDate, Vec<Event>>,
) -> gtk::ScrolledWindow {
    let vbox = gtk::Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .build();

    vbox.add_css_class("g-Calendar__Day");

    let gesture = gtk::GestureClick::new();
    gesture.connect_pressed(glib::clone!(@strong tx => move |_, n, _, _| {
        if n == 2 {
            update::send(tx.clone(), Msg::ShowAddForm { date });
        }
    }));
    vbox.add_controller(&gesture);

    if date == today {
        vbox.add_css_class("g-Calendar__Day--Today");
    }

    vbox.append(&day_label(date));

    let mut events = events
        .iter()
        .filter(|e| e.date == date)
        .collect::<Vec<&Event>>();
    let repeated_events = repetitions.get(&date).cloned().unwrap_or_default();
    events.extend(repeated_events.iter());
    events.sort_by_key(|e| e.start);

    if !events.is_empty() {
        vbox.append(&day_events(tx, events));
    }

    gtk::ScrolledWindow::builder()
        .hscrollbar_policy(gtk::PolicyType::Never)
        .hexpand(true)
        .vexpand(true)
        .child(&vbox)
        .build()
}

fn day_label(date: NaiveDate) -> gtk::Label {
    let label = gtk::Label::builder()
        .label(&format!(
            "{} {}",
            date.day(),
            MONTHES[date.month0() as usize]
        ))
        .halign(gtk::Align::Start)
        .build();

    label.add_css_class("g-Calendar__DayNumber");

    label
}

fn day_events(tx: Sender<Msg>, events: Vec<&Event>) -> gtk::Box {
    let vbox = gtk::Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .build();

    for event in events {
        let hbox = gtk::Box::builder()
            .orientation(gtk::Orientation::Horizontal)
            .hexpand(true)
            .build();

        let gesture = gtk::GestureClick::new();
        gesture.connect_pressed(
            glib::clone!(@strong event, @strong tx => move |gesture, n, _, _| {
                gesture.set_state(gtk::EventSequenceState::Claimed);
                if n == 2 {
                    update::send(tx.clone(), Msg::ShowUpdateForm { event: event.clone() });
                }
            }),
        );
        hbox.add_controller(&gesture);

        hbox.add_css_class("g-Calendar__DayEvent");

        let event_txt = &event.pprint();
        let label = gtk::Label::builder()
            .label(event_txt)
            .ellipsize(gtk::pango::EllipsizeMode::End)
            .tooltip_text(event_txt)
            .halign(gtk::Align::Start)
            .build();

        hbox.append(&label);
        vbox.append(&hbox);
    }

    vbox
}