use rocket::{
    form::Form,
    get, post,
    request::FlashMessage,
    response::{Flash, Redirect},
    routes, FromForm, Route, State,
};
use rocket_dyn_templates::Template;
use sqlx::SqlitePool;
use tera::Context;

use crate::{
    model::{
        boat::Boat,
        boatdamage::{BoatDamage, BoatDamageFixed, BoatDamageToAdd, BoatDamageVerified},
        user::{CoxUser, DonauLinzUser, TechUser, User, UserWithDetails},
    },
    tera::log::KioskCookie,
};

#[get("/")]
async fn index_kiosk(
    db: &State<SqlitePool>,
    flash: Option<FlashMessage<'_>>,
    _kiosk: KioskCookie,
) -> Template {
    let boatdamages = BoatDamage::all(db).await;
    let boats = Boat::all(db).await;
    let user = User::all(db).await;

    let mut context = Context::new();
    if let Some(msg) = flash {
        context.insert("flash", &msg.into_inner());
    }

    context.insert("boatdamages", &boatdamages);
    context.insert("boats", &boats);
    context.insert("user", &user);
    context.insert("show_kiosk_header", &true);

    Template::render("boatdamages", context.into_json())
}

#[get("/", rank = 2)]
async fn index(
    db: &State<SqlitePool>,
    flash: Option<FlashMessage<'_>>,
    user: DonauLinzUser,
) -> Template {
    let boatdamages = BoatDamage::all(db).await;
    let boats = Boat::all(db).await;

    let mut context = Context::new();
    if let Some(msg) = flash {
        context.insert("flash", &msg.into_inner());
    }

    context.insert("boatdamages", &boatdamages);
    context.insert("boats", &boats);
    context.insert(
        "loggedin_user",
        &UserWithDetails::from_user(user.into(), db).await,
    );

    Template::render("boatdamages", context.into_json())
}

#[derive(FromForm)]
pub struct FormBoatDamageToAdd<'r> {
    pub boat_id: i64,
    pub desc: &'r str,
    pub lock_boat: bool,
}

#[post("/", data = "<data>", rank = 2)]
async fn create<'r>(
    db: &State<SqlitePool>,
    data: Form<FormBoatDamageToAdd<'r>>,
    user: DonauLinzUser,
) -> Flash<Redirect> {
    let user: User = user.into();
    let boatdamage_to_add = BoatDamageToAdd {
        boat_id: data.boat_id,
        desc: data.desc,
        lock_boat: data.lock_boat,
        user_id_created: user.id as i32,
    };
    match BoatDamage::create(db, boatdamage_to_add).await {
        Ok(_) => Flash::success(
            Redirect::to("/boatdamage"),
            "Bootsschaden erfolgreich hinzugefügt",
        ),
        Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Fehler: {e}")),
    }
}

#[derive(FromForm)]
pub struct FormBoatDamageToAddKiosk<'r> {
    pub boat_id: i64,
    pub desc: &'r str,
    pub lock_boat: bool,
    pub user_id: i32,
}

#[post("/", data = "<data>")]
async fn create_from_kiosk<'r>(
    db: &State<SqlitePool>,
    data: Form<FormBoatDamageToAddKiosk<'r>>,
    _kiosk: KioskCookie,
) -> Flash<Redirect> {
    let boatdamage_to_add = BoatDamageToAdd {
        boat_id: data.boat_id,
        desc: data.desc,
        lock_boat: data.lock_boat,
        user_id_created: data.user_id,
    };
    match BoatDamage::create(db, boatdamage_to_add).await {
        Ok(_) => Flash::success(
            Redirect::to("/boatdamage"),
            "Bootsschaden erfolgreich hinzugefügt",
        ),
        Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Fehler: {e}")),
    }
}

#[derive(FromForm)]
pub struct FormBoatDamageFixed<'r> {
    pub desc: &'r str,
}

#[post("/<boatdamage_id>/fixed", data = "<data>")]
async fn fixed<'r>(
    db: &State<SqlitePool>,
    data: Form<FormBoatDamageFixed<'r>>,
    boatdamage_id: i32,
    coxuser: CoxUser,
) -> Flash<Redirect> {
    let boatdamage = BoatDamage::find_by_id(db, boatdamage_id).await.unwrap(); //TODO: Fix
    let boatdamage_fixed = BoatDamageFixed {
        desc: data.desc,
        user_id_fixed: coxuser.id as i32,
    };
    match boatdamage.fixed(db, boatdamage_fixed).await {
        Ok(_) => Flash::success(Redirect::to("/boatdamage"), "Bootsschaden behoben."),
        Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Error: {e}")),
    }
}

#[derive(FromForm)]
pub struct FormBoatDamageVerified<'r> {
    pub desc: &'r str,
}

#[post("/<boatdamage_id>/verified", data = "<data>")]
async fn verified<'r>(
    db: &State<SqlitePool>,
    data: Form<FormBoatDamageFixed<'r>>,
    boatdamage_id: i32,
    techuser: TechUser,
) -> Flash<Redirect> {
    let boatdamage = BoatDamage::find_by_id(db, boatdamage_id).await.unwrap(); //TODO: Fix
    let boatdamage_verified = BoatDamageVerified {
        desc: data.desc,
        user_id_verified: techuser.id as i32,
    };
    match boatdamage.verified(db, boatdamage_verified).await {
        Ok(_) => Flash::success(Redirect::to("/boatdamage"), "Bootsschaden verifiziert"),
        Err(e) => Flash::error(Redirect::to("/boatdamage"), format!("Error: {e}")),
    }
}

pub fn routes() -> Vec<Route> {
    routes![
        index,
        index_kiosk,
        create,
        fixed,
        verified,
        create_from_kiosk
    ]
}