use actix_web::{web, HttpResponse, Responder};
use rinja::Template;
use serde::Deserialize;
use sqlx::PgPool;

use crate::{
    endpoints::{assignment::PlanEventPersonalTablePartialTemplate, vehicle_assignment::PlanVehiclesPartialTemplate},
    models::{Assignment, Availabillity, Event, Function, Role, User, Vehicle, VehicleAssignement},
    utils::{
        event_planning_template::{
            generate_availabillity_assignment_list, generate_status_whether_staff_is_required,
            generate_vehicles_assigned_and_available,
        },
        ApplicationError,
    },
};

#[derive(Deserialize)]
pub struct VehicleAssignmentQuery {
    event: i32,
}

#[derive(Deserialize)]
pub struct VehicleAssignmentForm {
    vehicle: i32,
}

#[actix_web::post("/vehicleassignments/new")]
pub async fn post(
    user: web::ReqData<User>,
    pool: web::Data<PgPool>,
    form: web::Form<VehicleAssignmentForm>,
    query: web::Query<VehicleAssignmentQuery>,
) -> Result<impl Responder, ApplicationError> {
    let Some(event) = Event::read_by_id_including_location(pool.get_ref(), query.event).await?
    else {
        return Ok(HttpResponse::NotFound().finish());
    };

    let user_is_admin_or_area_manager_of_event_area = user.role == Role::Admin
        || (user.role == Role::AreaManager
            && user.area_id == event.location.as_ref().unwrap().area_id);

    if !user_is_admin_or_area_manager_of_event_area {
        return Err(ApplicationError::Unauthorized);
    }

    let Some(vehicle) = Vehicle::read(pool.get_ref(), form.vehicle).await? else {
        return Ok(HttpResponse::NotFound().finish());
    };

    let existing_assignments_for_vehicle =
        VehicleAssignement::read_all_by_vehicle(pool.get_ref(), vehicle.id).await?;
    let has_start_time_during_event =
        |a: &VehicleAssignement| a.start_time >= event.start_time && a.start_time <= event.end_time;
    let has_end_time_during_event =
        |a: &VehicleAssignement| a.end_time >= event.start_time && a.end_time <= event.end_time;

    let availability_already_assigned = existing_assignments_for_vehicle
        .iter()
        .any(|a| has_start_time_during_event(a) || has_end_time_during_event(a));

    if availability_already_assigned {
        return Ok(HttpResponse::BadRequest()
            .body("Vehicle already assigned to a timely conflicting event."));
    }

    VehicleAssignement::create(
        pool.get_ref(),
        event.id,
        vehicle.id,
        event.start_time,
        event.end_time,
    )
    .await?;

    let (vehicles_assigned, vehicles_available) =
        generate_vehicles_assigned_and_available(pool.get_ref(), &event).await?;
    let template = PlanVehiclesPartialTemplate {
        event,
        vehicles_assigned,
        vehicles_available,
    };

    Ok(HttpResponse::Ok().body(template.render()?))
}
