use std::{vec, collections::HashMap};

use actix_web::{web::{self, Json}, Responder, get};

use crate::{AppState, db};

pub(crate) mod schemas;

#[utoipa::path(
    params(

    ),
    responses(
        (status = 200, description = "OK", body = FullViewData),
    ),
    security(
        ("api_key" = [])
    ),
)]
#[get("/full_view_data")]
pub(crate) async fn full_view_data(
    data: web::Data<AppState>
) -> impl Responder {
    let start = chrono::Local::now(); // DEBUG

    // SQL Queries
    // TODO: Optimize by utilizing some SQL magic, for now this has to do

    let db_games: Vec<db::schemas::Game> = sqlx::query_as(
        "SELECT * FROM public.games ORDER BY id",
    )
    .fetch_all(&data.db)
    .await.expect("Error Fetching Data from DB");

    let db_groups: Vec<db::schemas::GameGroup> = sqlx::query_as(
        "SELECT * FROM public.game_groups ORDER BY id",
    )
    .fetch_all(&data.db)
    .await.expect("Error Fetching Data from DB");

    let db_empires: Vec<db::schemas::Empire> = sqlx::query_as(
        "SELECT * FROM public.empires ORDER BY id",
    )
    .fetch_all(&data.db)
    .await.expect("Error Fetching Data from DB");

    let db_ethics: Vec<db::schemas::Ethic> = sqlx::query_as(
        "SELECT * FROM public.ethics ORDER BY id",
    )
    .fetch_all(&data.db)
    .await.expect("Error Fetching Data from DB");

    let db_empire_ethics: Vec<db::schemas::EmpireEthic> = sqlx::query_as(
        "SELECT * FROM public.empire_ethics ORDER BY empires_id",
    )
    .fetch_all(&data.db)
    .await.expect("Error Fetching Data from DB");

    let db_portrait_groups: Vec<db::schemas::PortraitGroup> = sqlx::query_as(
        "SELECT * FROM public.portrait_groups ORDER BY id",
    )
    .fetch_all(&data.db)
    .await.expect("Error Fetching Data from DB");

    let db_portraits: Vec<db::schemas::Portrait> = sqlx::query_as(
        "SELECT * FROM public.portraits ORDER BY id",
    )
    .fetch_all(&data.db)
    .await.expect("Error Fetching Data from DB");

    let mut parsed_data: schemas::FullViewData = schemas::FullViewData {
        games: HashMap::new(),
        ethics: HashMap::new(),
        species: HashMap::new(),
    };

    // Data processing
    // Species Vector
    db_portrait_groups.iter().for_each(|species| {
        let new_data = schemas::Species {
            id: species.id,
            displayName: species.name.clone(),
            portraits: HashMap::new(),
        };

        parsed_data.species.entry(species.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
    });

    db_portraits.iter().for_each(|portrait| {
        let new_data = schemas::Portrait {
            id: portrait.id,
            hires: portrait.hires.clone(),
            lores: portrait.lores.clone(),
        };

        parsed_data.species.get_mut(&portrait.group_id).unwrap().portraits.entry(portrait.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
    });

    // Games Vector and Children
    db_games.iter().for_each(|game| {
        let new_data = schemas::ChellarisGame {
            id: game.id,
            name: game.name.clone(),
            empires: HashMap::new(),
            groups: HashMap::new(),
        };

        parsed_data.games.entry(game.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
    });

    db_groups.iter().for_each(|group| {
        let new_data = schemas::ChellarisGameGroup {
            id: group.id,
            name: group.name.clone(),
        };

        parsed_data.games.get_mut(&group.game_id).unwrap().groups.entry(group.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
    });

    db_empires.iter().for_each(|empire| {
        let new_data = schemas::ChellarisEmpire {
            id: empire.id,
            gestalt: empire.gestalt.unwrap_or(false),
            machine: false, // TODO overwrite this later on with correct data
            group: empire.group_id,
            empire_portrait: empire.empire_portrait_id,
            empire_portrait_group: empire.empire_portrait_group_id,
            discord_user: empire.discord_user.clone(),
            ethics: HashMap::new(),
        };

        parsed_data.games.get_mut(&empire.group_game_id).unwrap().empires.entry(empire.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
    });

    // Ethics Vector
    db_ethics.iter().for_each(|ethic| {
        let new_data = schemas::Ethic {
            id: ethic.id,
            displayName: ethic.name.clone(),
            machine: ethic.machine_ethic,
        };

        parsed_data.ethics.entry(ethic.id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
    });

    println!("{:#?}", parsed_data.ethics); // DEBUG

    db_empire_ethics.iter().for_each(|empire_ethic| {
        let game_id = empire_ethic.empires_group_game_id;
        let id = empire_ethic.empires_id;

        let new_data = schemas::EmpireEthic {
            id: empire_ethic.ethics_id,
            displayName: parsed_data.ethics[&empire_ethic.ethics_id].displayName.clone(),
            machine: parsed_data.ethics[&empire_ethic.ethics_id].machine,
            fanatic: empire_ethic.ethics_fanatic,
        };

        parsed_data.games.get_mut(&game_id).unwrap().empires.get_mut(&id).unwrap().ethics.entry(empire_ethic.ethics_id).and_modify(|d| *d = new_data.clone()).or_insert(new_data);
    });

    println!("{:?} ms", (chrono::Local::now() - start).to_std().unwrap()); // DEBUG
    
    Json(parsed_data)
}