import os
from typing import List, Optional

from fastapi import APIRouter, Body, HTTPException, Query

from boaviztapi import config, data_dir
from boaviztapi.dto.component import CPU, RAM, Disk, PowerSupply, Motherboard, Case
from boaviztapi.dto.component.cpu import mapper_cpu
from boaviztapi.dto.component.other import (
    mapper_motherboard,
    mapper_power_supply,
    mapper_case,
)
from boaviztapi.dto.component.ram import mapper_ram
from boaviztapi.dto.component.disk import mapper_ssd, mapper_hdd
from boaviztapi.model.component import Component
from boaviztapi.routers.openapi_doc.descriptions import (
    cpu_description,
    ram_description,
    ssd_description,
    hdd_description,
    motherboard_description,
    power_supply_description,
    case_description,
)
from boaviztapi.routers.openapi_doc.examples import components_examples_openapi
from boaviztapi.service.archetype import (
    get_component_archetype,
    get_device_archetype_lst,
)
from boaviztapi.service.impacts_computation import compute_impacts
from boaviztapi.service.verbose import verbose_component

component_router = APIRouter(prefix="/v1/component", tags=["component"])


@component_router.get("/all", description=cpu_description)
async def cpu_all_archetype_name():
    return {
        "cpu": "v1/component/cpu",
        "ram": "v1/component/ram",
        "ssd": "v1/component/ssd",
        "hdd": "v1/component/hdd",
        "motherboard": "v1/component/motherboard",
        "power_supply": "v1/component/power_supply",
        "case": "v1/component/case",
    }


@component_router.get("/cpu/archetype", description=cpu_description)
async def cpu_all_archetype_name():
    archetype_lst = get_all_archetype_name("cpu")
    return archetype_lst


@component_router.get("/cpu/archetype_config", description=cpu_description)
async def cpu_archetype_config(archetype: str = Query(example=config["default_cpu"])):
    archetype_config = get_archetype_config(archetype, "cpu")
    return archetype_config


@component_router.post("/cpu", description=cpu_description)
async def cpu_impact_bottom_up(
    cpu: CPU = Body(None, openapi_examples=components_examples_openapi["cpu"]),
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_cpu"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    archetype_config = get_component_archetype(archetype, "cpu")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    component = mapper_cpu(cpu, archetype_config)

    return await component_impact_bottom_up(
        component=component, verbose=verbose, duration=duration, criteria=criteria
    )


@component_router.get("/cpu", description=cpu_description)
async def cpu_impact_bottom_up(
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_cpu"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    archetype_config = get_component_archetype(archetype, "cpu")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    component = mapper_cpu(CPU(), archetype_config)

    return await component_impact_bottom_up(
        component=component, verbose=verbose, duration=duration, criteria=criteria
    )


@component_router.get("/ram/archetype", description=ram_description)
async def ram_all_archetype_name():
    archetype_lst = get_all_archetype_name("ram")
    return archetype_lst


@component_router.get("/ram/archetype_config", description=ram_description)
async def ram_archetype_config(archetype: str = Query(example=config["default_ram"])):
    archetype_config = get_archetype_config(archetype, "ram")
    return archetype_config


@component_router.post("/ram", description=ram_description)
async def ram_impact_bottom_up(
    ram: RAM = Body(None, openapi_examples=components_examples_openapi["ram"]),
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_ram"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    archetype_config = get_component_archetype(archetype, "ram")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    component = mapper_ram(ram, archetype_config)

    return await component_impact_bottom_up(
        component=component, verbose=verbose, duration=duration, criteria=criteria
    )


@component_router.get("/ram", description=ram_description)
async def ram_impact_bottom_up(
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_ram"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    archetype_config = get_component_archetype(archetype, "ram")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    component = mapper_ram(RAM(), archetype_config)

    return await component_impact_bottom_up(
        component=component, verbose=verbose, duration=duration, criteria=criteria
    )


@component_router.get("/ssd/archetype", description=ssd_description)
async def ssd_all_archetype_name():
    archetype_lst = get_all_archetype_name("ssd")
    return archetype_lst


@component_router.get("/ssd/archetype_config", description=ssd_description)
async def ssd_archetype_config(archetype: str = Query(example=config["default_ssd"])):
    archetype_config = get_archetype_config(archetype, "ssd")
    return archetype_config


@component_router.post("/ssd", description=ssd_description)
async def disk_impact_bottom_up(
    disk: Disk = Body(None, openapi_examples=components_examples_openapi["ssd"]),
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_ssd"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    disk.type = "ssd"
    archetype_config = get_component_archetype(archetype, "ssd")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    component = mapper_ssd(disk, archetype_config)

    return await component_impact_bottom_up(
        component=component, verbose=verbose, duration=duration, criteria=criteria
    )


@component_router.get("/ssd", description=ssd_description)
async def disk_impact_bottom_up(
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_ssd"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    disk = Disk()
    disk.type = "ssd"
    archetype_config = get_component_archetype(archetype, "ssd")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    component = mapper_ssd(disk, archetype_config)

    return await component_impact_bottom_up(
        component=component, verbose=verbose, duration=duration, criteria=criteria
    )


@component_router.get("/hdd/archetype", description=hdd_description)
async def hdd_all_archetype_name():
    archetype_lst = get_all_archetype_name("hdd")
    return archetype_lst


@component_router.get("/hdd/archetype_config", description=hdd_description)
async def hdd_archetype_config(archetype: str = Query(example=config["default_hdd"])):
    archetype_config = get_archetype_config(archetype, "hdd")
    return archetype_config


@component_router.post("/hdd", description=hdd_description)
async def disk_impact_bottom_up(
    disk: Disk = Body(None, openapi_examples=components_examples_openapi["hdd"]),
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_hdd"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    disk.type = "hdd"
    archetype_config = get_component_archetype(archetype, "hdd")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    component = mapper_hdd(disk, archetype_config)

    return await component_impact_bottom_up(
        component=component, verbose=verbose, duration=duration, criteria=criteria
    )


@component_router.get("/hdd", description=hdd_description)
async def disk_impact_bottom_up(
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_hdd"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    disk = Disk()
    disk.type = "hdd"
    archetype_config = get_component_archetype(archetype, "hdd")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    component = mapper_hdd(disk, archetype_config)

    return await component_impact_bottom_up(
        component=component, verbose=verbose, duration=duration, criteria=criteria
    )


@component_router.get("/motherboard/archetype", description=motherboard_description)
async def motherboard_all_archetype_name():
    archetype_lst = get_all_archetype_name("motherboard")
    return archetype_lst


@component_router.get(
    "/motherboard/archetype_config", description=motherboard_description
)
async def motherboard_archetype_config(
    archetype: str = Query(example=config["default_motherboard"]),
):
    archetype_config = get_archetype_config(archetype, "motherboard")
    return archetype_config


@component_router.post("/motherboard", description=motherboard_description)
async def motherboard_impact_bottom_up(
    motherboard: Motherboard = Body(
        None, openapi_examples=components_examples_openapi["motherboard"]
    ),
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    completed_motherboard = mapper_motherboard(motherboard)

    return await component_impact_bottom_up(
        component=completed_motherboard,
        verbose=verbose,
        duration=duration,
        criteria=criteria,
    )


@component_router.get("/motherboard", description=motherboard_description)
async def motherboard_impact_bottom_up(
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    completed_motherboard = mapper_motherboard(Motherboard())

    return await component_impact_bottom_up(
        component=completed_motherboard,
        verbose=verbose,
        duration=duration,
        criteria=criteria,
    )


@component_router.get("/power_supply/archetype", description=power_supply_description)
async def power_supply_all_archetype_name():
    archetype_lst = get_all_archetype_name("power_supply")
    return archetype_lst


@component_router.get(
    "/power_supply/archetype_config", description=power_supply_description
)
async def power_supply_archetype_config(
    archetype: str = Query(example=config["default_power_supply"]),
):
    archetype_config = get_archetype_config(archetype, "power_supply")
    return archetype_config


@component_router.post("/power_supply", description=power_supply_description)
async def power_supply_impact_bottom_up(
    power_supply: PowerSupply = Body(
        None, openapi_examples=components_examples_openapi["power_supply"]
    ),
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_power_supply"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    archetype_config = get_component_archetype(archetype, "power_supply")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    completed_power_supply = mapper_power_supply(power_supply, archetype_config)

    return await component_impact_bottom_up(
        component=completed_power_supply,
        verbose=verbose,
        duration=duration,
        criteria=criteria,
    )


@component_router.get("/power_supply", description=power_supply_description)
async def power_supply_impact_bottom_up(
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_power_supply"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    archetype_config = get_component_archetype(archetype, "power_supply")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    completed_power_supply = mapper_power_supply(PowerSupply(), archetype_config)

    return await component_impact_bottom_up(
        component=completed_power_supply,
        verbose=verbose,
        duration=duration,
        criteria=criteria,
    )


@component_router.get("/case/archetype", description=case_description)
async def case_all_archetype_name():
    archetype_lst = get_all_archetype_name("case")
    return archetype_lst


@component_router.get("/case/archetype_config", description=case_description)
async def case_archetype_config(archetype: str = Query(example=config["default_case"])):
    archetype_config = get_archetype_config(archetype, "case")
    return archetype_config


@component_router.post("/case", description=case_description)
async def case_impact_bottom_up(
    case: Case = Body(None, openapi_examples=components_examples_openapi["case"]),
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_case"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    archetype_config = get_component_archetype(archetype, "case")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    completed_case = mapper_case(case, archetype_config)

    return await component_impact_bottom_up(
        component=completed_case, verbose=verbose, duration=duration, criteria=criteria
    )


@component_router.get("/case", description=case_description)
async def case_impact_bottom_up(
    verbose: bool = True,
    duration: Optional[float] = config["default_duration"],
    archetype: str = config["default_case"],
    criteria: List[str] = Query(config["default_criteria"]),
):
    archetype_config = get_component_archetype(archetype, "case")

    if not archetype_config:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")

    completed_case = mapper_case(Case(), archetype_config)

    return await component_impact_bottom_up(
        component=completed_case, verbose=verbose, duration=duration, criteria=criteria
    )


async def component_impact_bottom_up(
    component: Component,
    verbose: bool,
    duration: Optional[float] = config["default_duration"],
    criteria=config["default_criteria"],
) -> dict:
    if duration is None:
        duration = component.usage.hours_life_time.value

    impacts = compute_impacts(
        model=component, duration=duration, selected_criteria=criteria
    )

    if verbose:
        return {
            "impacts": impacts,
            "verbose": verbose_component(component=component, duration=duration),
        }
    return {"impacts": impacts}


def get_all_archetype_name(name: str):
    return get_device_archetype_lst(
        os.path.join(data_dir, f"archetypes/components/{name.lower()}.csv")
    )


def get_archetype_config(archetype: str, component_type: str):
    result = get_component_archetype(archetype, component_type)
    if not result:
        raise HTTPException(status_code=404, detail=f"{archetype} not found")
    return result
