use chrono::NaiveTime;
use sqlx::{query, PgPool};

use super::{Function, Result};

pub struct Assignment {
    pub event_id: i32,
    pub availabillity_id: i32,
    pub function: Function,
    pub start_time: NaiveTime,
    pub end_time: NaiveTime,
}

impl Assignment {
    pub async fn create(
        pool: &PgPool,
        event_id: i32,
        availabillity_id: i32,
        function: Function,
        start_time: NaiveTime,
        end_time: NaiveTime,
    ) -> Result<()> {
        query!(
            r##"
                INSERT INTO assignment (eventId, availabillityId, function, startTime, endTime)
                VALUES ($1, $2, $3, $4, $5);
            "##,
            event_id,
            availabillity_id,
            function as Function,
            start_time,
            end_time
        )
        .execute(pool)
        .await?;

        Ok(())
    }

    pub async fn read_all_by_availabillity(
        pool: &PgPool,
        availabillity_id: i32,
    ) -> Result<Vec<Assignment>> {
        let records = query!(
            r##"
                SELECT
                    assignment.eventId,
                    assignment.availabillityId,
                    assignment.function AS "function: Function",
                    assignment.startTime,
                    assignment.endTime
                FROM assignment
                WHERE assignment.AvailabillityId = $1;
            "##,
            availabillity_id
        )
        .fetch_all(pool)
        .await?;

        let assignemnets = records
            .iter()
            .map(|r| Assignment {
                event_id: r.eventid,
                availabillity_id: r.availabillityid,
                function: r.function,
                start_time: r.starttime,
                end_time: r.endtime,
            })
            .collect();

        Ok(assignemnets)
    }

    pub async fn read_all_by_event(pool: &PgPool, event_id: i32) -> Result<Vec<Assignment>> {
        let records = query!(
            r##"
                SELECT
                    assignment.eventId,
                    assignment.availabillityId,
                    assignment.function AS "function: Function",
                    assignment.startTime,
                    assignment.endTime
                FROM assignment
                WHERE assignment.eventId = $1;
            "##,
            event_id,
        )
        .fetch_all(pool)
        .await?;

        let assignemnets = records
            .iter()
            .map(|r| Assignment {
                event_id: r.eventid,
                availabillity_id: r.availabillityid,
                function: r.function,
                start_time: r.starttime,
                end_time: r.endtime,
            })
            .collect();

        Ok(assignemnets)
    }

    pub async fn count_by_event_and_function(
        pool: &PgPool,
        event_id: i32,
        function: Function,
    ) -> Result<i64> {
        let result = query!("SELECT count(*) FROM assignment WHERE assignment.eventId = $1 AND assignment.function = $2;",
            event_id,
            function as Function
        )
        .fetch_one(pool)
        .await?;

        Ok(result.count.unwrap_or(0))
    }

    pub async fn read(
        pool: &PgPool,
        event_id: i32,
        availabillity_id: i32,
    ) -> Result<Option<Assignment>> {
        let record = query!(
            r##"
                SELECT
                    assignment.eventId,
                    assignment.availabillityId,
                    assignment.function AS "function: Function",
                    assignment.startTime,
                    assignment.endTime
                FROM assignment
                WHERE
                    assignment.eventId = $1 AND
                    assignment.availabillityId = $2;
            "##,
            event_id,
            availabillity_id
        )
        .fetch_optional(pool)
        .await?;

        let assignemnet = record.and_then(|r| {
            Some(Assignment {
                event_id: r.eventid,
                availabillity_id: r.availabillityid,
                function: r.function,
                start_time: r.starttime,
                end_time: r.endtime,
            })
        });

        Ok(assignemnet)
    }

    pub async fn delete(pool: &PgPool, event_id: i32, availabillity_id: i32) -> Result<()> {
        query!("DELETE FROM assignment WHERE assignment.eventId = $1 AND assignment.availabillityId = $2;",
            event_id,
            availabillity_id
        )
        .execute(pool)
        .await?;
        Ok(())
    }
}
