use chrono::NaiveDate;
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,
        boatreservation::{BoatReservation, BoatReservationToAdd},
        user::{DonauLinzUser, User, UserWithRoles},
    },
    tera::log::KioskCookie,
};

#[get("/")]
async fn index_kiosk(
    db: &State<SqlitePool>,
    flash: Option<FlashMessage<'_>>,
    _kiosk: KioskCookie,
) -> Template {
    let boatreservations = BoatReservation::all_future(db).await;

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

    let linz_boats = Boat::all_for_boatshouse(db).await;
    let mut boats = Vec::new();
    for boat in linz_boats {
        if boat.boat.owner.is_none() {
            boats.push(boat);
        }
    }

    context.insert("boatreservations", &boatreservations);
    context.insert("boats", &boats);
    context.insert("user", &User::all(db).await);
    context.insert("show_kiosk_header", &true);

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

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

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

    let linz_boats = Boat::all_for_boatshouse(db).await;
    let mut boats = Vec::new();
    for boat in linz_boats {
        if boat.boat.owner.is_none() {
            boats.push(boat);
        }
    }

    context.insert("boatreservations", &boatreservations);
    context.insert("boats", &boats);
    context.insert("user", &User::all(db).await);
    context.insert(
        "loggedin_user",
        &UserWithRoles::from_user(user.into(), db).await,
    );

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

#[derive(Debug, FromForm)]
pub struct FormBoatReservationToAdd<'r> {
    pub boat_id: i64,
    pub start_date: &'r str,
    pub end_date: &'r str,
    pub time_desc: &'r str,
    pub usage: &'r str,
    pub user_id_applicant: Option<i64>,
}

#[post("/", data = "<data>", rank = 2)]
async fn create<'r>(
    db: &State<SqlitePool>,
    data: Form<FormBoatReservationToAdd<'r>>,
    user: DonauLinzUser,
) -> Flash<Redirect> {
    let user_applicant: User = user.into();
    let boat = Boat::find_by_id(db, data.boat_id as i32).await.unwrap();
    let boatreservation_to_add = BoatReservationToAdd {
        boat: &boat,
        start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
        end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
        time_desc: data.time_desc,
        usage: data.usage,
        user_applicant: &user_applicant,
    };
    match BoatReservation::create(db, boatreservation_to_add).await {
        Ok(_) => Flash::success(
            Redirect::to("/boatreservation"),
            "Reservierung erfolgreich hinzugefügt",
        ),
        Err(e) => Flash::error(Redirect::to("/boatreservation"), format!("Fehler: {e}")),
    }
}

#[post("/", data = "<data>")]
async fn create_from_kiosk<'r>(
    db: &State<SqlitePool>,
    data: Form<FormBoatReservationToAdd<'r>>,
    _kiosk: KioskCookie,
) -> Flash<Redirect> {
    let user_applicant: User = User::find_by_id(db, data.user_id_applicant.unwrap() as i32)
        .await
        .unwrap();
    let boat = Boat::find_by_id(db, data.boat_id as i32).await.unwrap();
    let boatreservation_to_add = BoatReservationToAdd {
        boat: &boat,
        start_date: NaiveDate::parse_from_str(data.start_date, "%Y-%m-%d").unwrap(),
        end_date: NaiveDate::parse_from_str(data.end_date, "%Y-%m-%d").unwrap(),
        time_desc: data.time_desc,
        usage: data.usage,
        user_applicant: &user_applicant,
    };
    match BoatReservation::create(db, boatreservation_to_add).await {
        Ok(_) => Flash::success(
            Redirect::to("/boatreservation"),
            "Reservierung erfolgreich hinzugefügt",
        ),
        Err(e) => Flash::error(Redirect::to("/boatreservation"), format!("Fehler: {e}")),
    }
}

#[get("/<reservation_id>/delete")]
async fn delete<'r>(
    db: &State<SqlitePool>,
    reservation_id: i32,
    user: DonauLinzUser,
) -> Flash<Redirect> {
    let reservation = BoatReservation::find_by_id(db, reservation_id)
        .await
        .unwrap();

    if user.id == reservation.user_id_applicant || user.has_role(db, "admin").await {
        reservation.delete(db).await;
        Flash::success(
            Redirect::to("/boatreservation"),
            "Reservierung erfolgreich gelöscht",
        )
    } else {
        Flash::error(
            Redirect::to("/boatreservation"),
            format!("Nur der Reservierer darf die Reservierung löschen."),
        )
    }
}

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