use sqlx::{query, PgPool};

use super::Area;

use super::Result;

#[derive(Debug)]
pub struct Location {
    pub id: i32,
    pub name: String,
    pub area_id: i32,
    pub area: Option<Area>,
}

impl Location {
    pub async fn create(pool: &PgPool, name: &str, area_id: i32) -> Result<()> {
        query!(
            "INSERT INTO location (name, areaId) VALUES ($1, $2);",
            name,
            area_id
        )
        .execute(pool)
        .await?;

        Ok(())
    }

    pub async fn read_by_area(pool: &PgPool, area_id: i32) -> Result<Vec<Location>> {
        let records = query!("SELECT * FROM location WHERE areaId = $1;", area_id)
            .fetch_all(pool)
            .await?;

        let locations = records
            .iter()
            .map(|lr| Location {
                id: lr.id,
                name: lr.name.to_string(),
                area_id: lr.areaid,
                area: None,
            })
            .collect();

        Ok(locations)
    }

    pub async fn read_all(pool: &PgPool) -> Result<Vec<Location>> {
        let records = query!("SELECT * FROM location").fetch_all(pool).await?;

        let locations = records
            .iter()
            .map(|lr| Location {
                id: lr.id,
                name: lr.name.to_string(),
                area_id: lr.areaid,
                area: None,
            })
            .collect();

        Ok(locations)
    }

    pub async fn read_all_including_area(pool: &PgPool) -> Result<Vec<Location>> {
        let records = query!("SELECT location.id AS locationId, location.name, location.areaId, area.id, area.name AS areaName FROM location JOIN area ON location.areaId = area.id;")
            .fetch_all(pool)
            .await?;

        let locations = records
            .iter()
            .map(|lr| Location {
                id: lr.locationid,
                name: lr.name.to_string(),
                area_id: lr.areaid,
                area: Some(Area {
                    id: lr.id,
                    name: lr.areaname.to_string(),
                }),
            })
            .collect();

        Ok(locations)
    }

    pub async fn read_by_id(pool: &PgPool, id: i32) -> Result<Option<Location>> {
        let record = query!("SELECT * FROM location WHERE id = $1;", id)
            .fetch_optional(pool)
            .await?;

        let location = record.and_then(|r| {
            Some(Location {
                id: r.id,
                name: r.name,
                area_id: r.areaid,
                area: None,
            })
        });

        Ok(location)
    }

    pub async fn update(pool: &PgPool, id: i32, name: &str, area_id: i32) -> Result<()> {
        query!(
            "UPDATE location SET name = $1, areaid = $2 WHERE id = $3;",
            name,
            area_id,
            id
        )
        .execute(pool)
        .await?;

        Ok(())
    }

    pub async fn delete(pool: &PgPool, id: i32) -> Result<()> {
        query!("DELETE FROM location WHERE id = $1;", id)
            .execute(pool)
            .await?;

        Ok(())
    }
}
