Documentation
use crate::{Endpoint, LDateTime, types::*};
use chrono::Local;
use serde::{Deserialize, Serialize};

#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Lesson {
    pub uid: String,
    pub datum: LDateTime,
    pub kezdet_idopont: LDateTime,
    pub veg_idopont: LDateTime,
    pub nev: String,
    pub oraszam: Option<u8>,
    pub ora_eves_sorszama: Option<u32>,
    pub osztaly_csoport: Option<Uid>,
    pub tanar_neve: Option<String>,
    pub tantargy: Option<Tantargy>,
    pub tema: Option<String>,
    pub terem_neve: Option<String>,
    pub tipus: Rektip,
    pub tanulo_jelenlet: Option<Rektip>,
    pub allapot: Option<Rektip>,
    pub helyettes_tanar_neve: Option<String>,
    pub hazi_feladat_uid: Option<String>,
    pub feladat_group_uid: Option<String>,
    pub nyelvi_feladat_group_uid: Option<String>,
    pub bejelentett_szamonkeres_uid: Option<String>,
    pub is_tanulo_hazi_feladat_enabled: bool,
    pub is_hazi_feladat_megoldva: bool,
    pub csatolmanyok: Vec<String>,
    pub is_digitalis_ora: bool,
    pub digitalis_eszkoz_tipus: Option<String>,
    pub digitalis_platform_tipus: Option<String>,
    pub digitalis_tamogato_eszkoz_tipus_list: Option<Vec<String>>,
    pub letrehozas: String,
    pub utolso_modositas: chrono::NaiveDateTime,
}
impl Lesson {
    /// Get the day of this [`Lesson`].
    pub fn date_naive(&self) -> chrono::NaiveDate {
        self.kezdet_idopont.date_naive()
    }
    /// Minutes until the start of this [`Lesson`].
    pub fn mins_till_start(&self) -> i64 {
        (self.kezdet_idopont - Local::now()).num_minutes()
    }
    /// Minutes until the end of this [`Lesson`].
    pub fn mins_till_end(&self) -> i64 {
        (self.veg_idopont - Local::now()).num_minutes()
    }
    /// Normalise the name of the [room][`Lesson::terem_neve`], empty [`String`] on [`None`].
    pub fn normalised_room(&self) -> String {
        if let Some(room) = self.terem_neve.clone() {
            room.replace("terem", "").trim().to_string()
        } else {
            String::new()
        }
    }
    /// The two goddamn [`Lesson`]s should happen in the same time.
    pub fn same_time(&self, other: &Self) -> bool {
        self.kezdet_idopont == other.kezdet_idopont && self.veg_idopont == other.veg_idopont
    }
    /// Returns whether this [`Lesson`] has been/will be cancelled.
    pub fn cancelled(&self) -> bool {
        self.allapot.as_ref().is_some_and(|a| a.nev == "Elmaradt")
    }
    /// Returns whether the student has appeared on this [`Lesson`].
    pub fn absent(&self) -> bool {
        self.tanulo_jelenlet
            .as_ref()
            .is_some_and(|absence| absence.nev == "Hianyzas")
    }
    /// Returns the subject id of this [`Lesson`].
    pub fn subject_id(&self) -> Option<&String> {
        self.tantargy.as_ref().map(|tt| &tt.kategoria.nev)
    }
    /// Returns whether this [`Lesson`] is just false positive, meaning it's just a title for a day.
    pub fn kamu_smafu(&self) -> bool {
        self.kezdet_idopont
            .signed_duration_since(self.veg_idopont)
            .is_zero()
    }
    /// Returns whether this [`Lesson`] is currently happening.
    pub fn happening(&self) -> bool {
        if self.cancelled() {
            return false;
        }
        self.kezdet_idopont <= Local::now() && self.veg_idopont >= Local::now()
    }

    /// Returns whether this [`Lesson`] is a forecoming one: to be done.
    pub fn forecoming(&self) -> bool {
        self.kezdet_idopont > Local::now()
    }
    /// Shows which lesson of the day it is, eg.: 4th. if [`None`] => [`u8::MAX`]
    pub fn d_num(&self) -> u8 {
        self.oraszam.unwrap_or(u8::MAX)
    }
}

impl Endpoint for Lesson {
    type Args = (chrono::NaiveDate, chrono::NaiveDate);

    fn path(_args: &Self::Args) -> String {
        "/ellenorzo/V3/Sajat/OrarendElemek".into()
    }

    fn query(input: Self::Args) -> Vec<(&'static str, String)> {
        let from = input.0.and_hms_opt(0, 0, 0).unwrap().to_string();
        let to = input.1.and_hms_opt(23, 59, 59).unwrap().to_string();
        vec![("datumTol", from), ("datumIg", to)]
    }

    fn when(&self) -> Option<crate::LDateTime> {
        Some(self.kezdet_idopont)
    }
}