use std::collections::HashMap;

use rocket::serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};

use crate::tera::board::boathouse::FormBoathouseToAdd;

use super::boat::Boat;

#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct Boathouse {
    pub id: i64,
    pub boat_id: i64,
    pub aisle: String,
    pub side: String,
    pub level: i64,
}

impl Boathouse {
    pub async fn get(db: &SqlitePool) -> HashMap<&str, HashMap<&str, [Option<(i64, Boat)>; 12]>> {
        let mut ret: HashMap<&str, HashMap<&str, [Option<(i64, Boat)>; 12]>> = HashMap::new();

        let mut mountain = HashMap::new();
        mountain.insert(
            "mountain",
            [
                None, None, None, None, None, None, None, None, None, None, None, None,
            ],
        );
        mountain.insert(
            "water",
            [
                None, None, None, None, None, None, None, None, None, None, None, None,
            ],
        );
        ret.insert("mountain-aisle", mountain);

        let mut middle = HashMap::new();
        middle.insert(
            "mountain",
            [
                None, None, None, None, None, None, None, None, None, None, None, None,
            ],
        );
        middle.insert(
            "water",
            [
                None, None, None, None, None, None, None, None, None, None, None, None,
            ],
        );
        ret.insert("middle-aisle", middle);

        let mut water = HashMap::new();
        water.insert(
            "mountain",
            [
                None, None, None, None, None, None, None, None, None, None, None, None,
            ],
        );
        water.insert(
            "water",
            [
                None, None, None, None, None, None, None, None, None, None, None, None,
            ],
        );
        ret.insert("water-aisle", water);

        let boathouses = sqlx::query_as!(
            Boathouse,
            "SELECT id, boat_id, aisle, side, level FROM boathouse"
        )
        .fetch_all(db)
        .await
        .unwrap(); //TODO: fixme

        for boathouse in boathouses {
            let aisle = ret
                .get_mut(format!("{}-aisle", boathouse.aisle).as_str())
                .unwrap();
            let side = aisle.get_mut(boathouse.side.as_str()).unwrap();

            side[boathouse.level as usize] = Some((
                boathouse.id,
                Boat::find_by_id(db, boathouse.boat_id as i32)
                    .await
                    .unwrap(),
            ));
        }

        ret
    }

    pub async fn create(db: &SqlitePool, data: FormBoathouseToAdd) -> Result<(), String> {
        sqlx::query!(
            "INSERT INTO boathouse(boat_id, aisle, side, level) VALUES (?,?,?,?)",
            data.boat_id,
            data.aisle,
            data.side,
            data.level
        )
        .execute(db)
        .await
        .map_err(|e| e.to_string())?;
        Ok(())
    }

    pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
        sqlx::query_as!(Self, "SELECT * FROM boathouse WHERE id like ?", id)
            .fetch_one(db)
            .await
            .ok()
    }

    pub async fn delete(&self, db: &SqlitePool) {
        sqlx::query!("DELETE FROM boathouse WHERE id=?", self.id)
            .execute(db)
            .await
            .unwrap(); //Okay, because we can only create a Boat of a valid id
    }
}
