use crate::model::{logbook::Logbook, stat::Stat, user::User};
use serde::Serialize;

#[derive(Serialize, PartialEq, Debug)]
pub(crate) enum Level {
    None,
    Bronze,
    Silver,
    Gold,
    Diamond,
    Done,
}

impl Level {
    fn required_km(&self) -> i32 {
        match self {
            Level::Bronze => 40_000,
            Level::Silver => 80_000,
            Level::Gold => 100_000,
            Level::Diamond => 200_000,
            Level::Done => 0,
            Level::None => 0,
        }
    }

    fn next_level(km: i32) -> Self {
        if km < Level::Bronze.required_km() {
            Level::Bronze
        } else if km < Level::Silver.required_km() {
            Level::Silver
        } else if km < Level::Gold.required_km() {
            Level::Gold
        } else if km < Level::Diamond.required_km() {
            Level::Diamond
        } else {
            Level::Done
        }
    }

    pub(crate) fn curr_level(km: i32) -> Self {
        if km < Level::Bronze.required_km() {
            Level::None
        } else if km < Level::Silver.required_km() {
            Level::Bronze
        } else if km < Level::Gold.required_km() {
            Level::Silver
        } else if km < Level::Diamond.required_km() {
            Level::Gold
        } else {
            Level::Diamond
        }
    }

    pub(crate) fn desc(&self) -> &str {
        match self {
            Level::Bronze => "Bronze",
            Level::Silver => "Silber",
            Level::Gold => "Gold",
            Level::Diamond => "Diamant",
            Level::Done => "",
            Level::None => "-",
        }
    }
}

#[derive(Serialize)]
pub(crate) struct Next {
    level: Level,
    desc: String,
    missing_km: i32,
    required_km: i32,
    rowed_km: i32,
}

impl Next {
    pub(crate) fn new(rowed_km: i32) -> Self {
        let level = Level::next_level(rowed_km);
        let required_km = level.required_km();
        let missing_km = required_km - rowed_km;
        Self {
            desc: level.desc().to_string(),
            level,
            missing_km,
            required_km,
            rowed_km,
        }
    }
}

pub(crate) async fn new_level_with_last_log(
    db: &mut sqlx::Transaction<'_, sqlx::Sqlite>,
    user: &User,
) -> Option<String> {
    let rowed_km = Stat::total_km_tx(db, user).await.rowed_km;

    if let Some(last_logbookentry) = Logbook::completed_with_user_tx(db, user).await.last() {
        let last_trip_km = last_logbookentry.logbook.distance_in_km.unwrap();
        if Level::curr_level(rowed_km) != Level::curr_level(rowed_km - last_trip_km as i32) {
            return Some(Level::curr_level(rowed_km).desc().to_string());
        }
    }

    None
}