use sqlx::PgPool;

use crate::models::{Assignment, Availabillity, AvailabillityAssignmentState, Event, Function};

use super::ApplicationError;

pub async fn generate_availabillity_assignment_list(
    pool: &PgPool,
    event: &Event,
) -> Result<Vec<(Availabillity, AvailabillityAssignmentState)>, ApplicationError> {
    let availabillities_in_db = Availabillity::read_by_date_and_area_including_user(
        pool,
        event.date,
        event.location.as_ref().unwrap().area_id,
    )
    .await?;

    let mut availabillities = Vec::new();
    for availabillity in availabillities_in_db {
        let assignments = Assignment::read_all_by_availabillity(pool, availabillity.id).await?;

        if let Some(assignment) = assignments
            .iter()
            .find(|assignment| assignment.event_id == event.id)
        {
            let state = match assignment.function {
                Function::Posten => {
                    AvailabillityAssignmentState::AssignedPosten(assignment.availabillity_id)
                }
                Function::Fuehrungsassistent => {
                    AvailabillityAssignmentState::AssignedFührungsassistent(
                        assignment.availabillity_id,
                    )
                }
                Function::Wachhabender => {
                    AvailabillityAssignmentState::AssignedWachahabender(assignment.availabillity_id)
                }
            };

            availabillities.push((availabillity, state));
            continue;
        }

        let has_start_time_during_event =
            |a: &Assignment| a.start_time >= event.start_time && a.start_time <= event.end_time;
        let has_end_time_during_event =
            |a: &Assignment| a.end_time >= event.start_time && a.end_time <= event.end_time;

        if assignments
            .iter()
            .any(|a| has_start_time_during_event(a) || has_end_time_during_event(a))
        {
            availabillities.push((availabillity, AvailabillityAssignmentState::Conflicting));
            continue;
        }

        availabillities.push((availabillity, AvailabillityAssignmentState::Unassigned));
    }

    //println!(" {availabillities:#?}");
    Ok(availabillities)
}

pub async fn generate_status_whether_staff_is_required(
    pool: &PgPool,
    event: &Event,
) -> Result<(bool, bool, bool), ApplicationError> {
    let existing_assignments_for_event = Assignment::read_all_by_event(pool, event.id).await?;

    let further_posten_required = existing_assignments_for_event
        .iter()
        .filter(|a| a.function == Function::Posten)
        .count()
        < event.amount_of_posten as usize;

    // TODO change to Fuehrungsassistent
    let further_fuehrungsassistent_required = event.voluntary_wachhabender
        && existing_assignments_for_event
            .iter()
            .all(|a| a.function != Function::Fuehrungsassistent);

    let further_wachhabender_required = event.voluntary_wachhabender
        && existing_assignments_for_event
            .iter()
            .all(|a| a.function != Function::Wachhabender);

    //println!("further_posten {further_posten_required}");
    Ok((
        further_posten_required,
        further_fuehrungsassistent_required,
        further_wachhabender_required,
    ))
}
