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

use super::{Location, Result};

#[derive(Debug)]
pub struct Event {
    pub id: i32,
    pub date: NaiveDate,
    pub start_time: NaiveTime,
    pub end_time: NaiveTime,
    pub name: String,
    pub location_id: i32,
    pub location: Option<Location>,
    pub voluntary_wachhabender: bool,
    pub amount_of_posten: i16,
    pub clothing: String,
    pub canceled: bool,
    pub note: Option<String>,
}

impl Event {
    pub async fn create(
        pool: &PgPool,
        date: &NaiveDate,
        start_time: &NaiveTime,
        end_time: &NaiveTime,
        name: &String,
        location_id: i32,
        voluntary_wachhabender: bool,
        amount_of_posten: i16,
        clothing: &String,
        note: Option<&String>,
    ) -> Result<()> {
        query!(r#"
            INSERT INTO event (date, startTime, endTime, name, locationId, voluntaryWachhabender, amountOfPosten, clothing, note)
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);
            "#,
            date, start_time, end_time, name, location_id, voluntary_wachhabender, amount_of_posten, clothing, note).execute(pool).await?;

        Ok(())
    }

    pub async fn read_all_by_date_and_area_including_location(
        pool: &PgPool,
        date: NaiveDate,
        area_id: i32,
    ) -> Result<Vec<Event>> {
        let records = query!(
            r#"
            SELECT
                event.id AS eventId,
                event.date,
                event.startTime,
                event.endTime,
                event.name,
                event.locationId,
                event.voluntaryWachhabender,
                event.amountOfPosten,
                event.clothing,
                event.canceled,
                event.note,
                location.id,
                location.name AS locationName,
                location.areaId AS locationAreaId
            FROM event
            JOIN location ON event.locationId = location.id
            WHERE date = $1
            AND location.areaId = $2;
            "#,
            date,
            area_id
        )
        .fetch_all(pool)
        .await?;

        let events = records
            .into_iter()
            .map(|record| Event {
                id: record.eventid,
                date: record.date,
                start_time: record.starttime,
                end_time: record.endtime,
                name: record.name.to_string(),
                location_id: record.locationid,
                location: Some(Location {
                    id: record.locationid,
                    name: record.locationname.to_string(),
                    area_id: record.locationareaid,
                    area: None,
                }),
                voluntary_wachhabender: record.voluntarywachhabender,
                amount_of_posten: record.amountofposten,
                clothing: record.clothing.to_string(),
                canceled: record.canceled,
                note: record.note,
            })
            .collect();

        Ok(events)
    }

    pub async fn read_by_id_including_location(pool: &PgPool, id: i32) -> Result<Option<Event>> {
        let record = query!(
            r#"
            SELECT
                event.id AS eventId,
                event.date,
                event.startTime,
                event.endTime,
                event.name,
                event.locationId,
                event.voluntaryWachhabender,
                event.amountOfPosten,
                event.clothing,
                event.canceled,
                event.note,
                location.id,
                location.name AS locationName,
                location.areaId AS locationAreaId
            FROM event
            JOIN location ON event.locationId = location.id
            WHERE event.id = $1;
            "#,
            id
        )
        .fetch_optional(pool)
        .await?;

        let event = record.and_then(|record| {
            Some(Event {
                id: record.eventid,
                date: record.date,
                start_time: record.starttime,
                end_time: record.endtime,
                name: record.name.to_string(),
                location_id: record.locationid,
                location: Some(Location {
                    id: record.locationid,
                    name: record.locationname.to_string(),
                    area_id: record.locationareaid,
                    area: None,
                }),
                voluntary_wachhabender: record.voluntarywachhabender,
                amount_of_posten: record.amountofposten,
                clothing: record.clothing.to_string(),
                canceled: record.canceled,
                note: record.note,
            })
        });

        Ok(event)
    }
}
