from typing import Tuple, Union, Optional

from boaviztapi import config
from boaviztapi.model import ComputedImpacts
from boaviztapi.model.device.server import DeviceServer
from boaviztapi.model.services.cloud_instance import Service, ServiceCloudInstance
from boaviztapi.model.component import (
    ComponentCPU,
    ComponentCase,
    ComponentPowerSupply,
    ComponentRAM,
    Component,
    ComponentAssembly,
    ComponentHDD,
    ComponentSSD,
    ComponentMotherboard,
)
from boaviztapi.model.component.functional_block import ComponentFunctionalBlock
from boaviztapi.model.device import Device
from boaviztapi.model.device.iot import DeviceIoT
from boaviztapi.model.impact import (
    ImpactFactor,
    IMPACT_PHASES,
    IMPACT_CRITERIAS,
    Impact,
    USE,
)
from boaviztapi.service.factor_provider import get_impact_factor, get_iot_impact_factor


def compute_single_impact(
    model: Union[Component, Device, Service],
    phase: str,
    criteria: str,
    duration: Union[int, str] = config["default_duration"],
    allocation: float = 1,
) -> Optional[Impact]:
    try:
        impact_function = get_impact_function(model, phase)

        impact, min_impact, max_impact, warnings = impact_function(
            criteria, duration, model
        )

        result = Impact(
            value=impact * allocation,
            min=min_impact * allocation,
            max=max_impact * allocation,
            warnings=list(set(warnings)),
        )

        model.add_impacts(result, criteria, phase)

        return result
    except (AttributeError, NotImplementedError):
        model.add_impacts(None, criteria, phase)
        return None


def compute_impacts(
    model: Union[Component, Device, Service],
    selected_criteria=config["default_criteria"],
    duration=config["default_duration"],
) -> dict:
    for c in IMPACT_CRITERIAS.keys():
        criteria = IMPACT_CRITERIAS[c]
        if "all" not in selected_criteria:
            if criteria.name not in selected_criteria:
                continue
        for phase in IMPACT_PHASES:
            compute_single_impact(model, phase, criteria.name, duration)

    return model.get_impacts(selected_criteria)


def get_impact_function(model: Union[Component, Device, Service], phase: str):
    return impacts_functions[model.NAME][phase]


def not_implemented_function(
    impact_type: str, duration: int, model: Union[Component, Device, Service]
):
    raise NotImplementedError


def simple_impact_use(
    impact_type: str, duration: int, model: Union[Component, Device, Service]
) -> ComputedImpacts:
    if not model.usage.avg_power.has_value():
        raise NotImplementedError

    impact_factor = model.usage.elec_factors[impact_type]

    impacts = (
        impact_factor.value
        * (model.usage.avg_power.value / 1000)
        * model.usage.use_time_ratio.value
        * duration
        * model.units.value
    )
    max_impact = (
        impact_factor.max
        * (model.usage.avg_power.max / 1000)
        * model.usage.use_time_ratio.min
        * duration
        * model.units.max
    )
    min_impact = (
        impact_factor.min
        * (model.usage.avg_power.min / 1000)
        * model.usage.use_time_ratio.max
        * duration
        * model.units.min
    )

    return impacts, min_impact, max_impact, []


def simple_embedded(
    impact_type: str, duration: int, model: [Device, Component, Service]
) -> ComputedImpacts:
    if hasattr(model, "type") and model.type is not None:
        impact = Impact(
            value=float(
                get_impact_factor(item=model.NAME, impact_type=impact_type)[
                    model.type.value
                ]["impact"]
            )
            * model.units.value,
            min=float(
                get_impact_factor(item=model.NAME, impact_type=impact_type)[
                    model.type.value
                ]["impact"]
            )
            * model.units.min,
            max=float(
                get_impact_factor(item=model.NAME, impact_type=impact_type)[
                    model.type.value
                ]["impact"]
            )
            * model.units.max,
        )
    else:
        impact = Impact(
            value=float(
                get_impact_factor(item=model.NAME, impact_type=impact_type)["impact"]
            )
            * model.units.value,
            min=float(
                get_impact_factor(item=model.NAME, impact_type=impact_type)["impact"]
            )
            * model.units.min,
            max=float(
                get_impact_factor(item=model.NAME, impact_type=impact_type)["impact"]
            )
            * model.units.max,
        )

    impact.allocate(duration, model.usage.hours_life_time)

    warnings = ["Generic data used for impact calculation."]

    return impact.value, impact.min, impact.max, warnings


def cpu_impact_use(
    impact_type: str, duration: int, cpu: ComponentCPU
) -> ComputedImpacts:
    impact_factor = cpu.usage.elec_factors[impact_type]

    if not cpu.usage.avg_power.is_set():
        modeled_consumption = cpu.model_power_consumption()
        cpu.usage.avg_power.set_completed(
            modeled_consumption.value,
            min=modeled_consumption.min,
            max=modeled_consumption.max,
        )

    impact = Impact(
        value=impact_factor.value
        * (cpu.usage.avg_power.value / 1000)
        * cpu.usage.use_time_ratio.value
        * duration,
        min=impact_factor.min
        * (cpu.usage.avg_power.min / 1000)
        * cpu.usage.use_time_ratio.min
        * duration,
        max=impact_factor.max
        * (cpu.usage.avg_power.max / 1000)
        * cpu.usage.use_time_ratio.max
        * duration,
    )

    return impact.value, impact.min, impact.max, []


def cpu_impact_embedded(
    impact_type: str, duration: int, cpu: ComponentCPU
) -> ComputedImpacts:
    cpu_die_impact = Impact(
        value=get_impact_factor(item="cpu", impact_type=impact_type)["die_impact"],
        min=get_impact_factor(item="cpu", impact_type=impact_type)["die_impact"],
        max=get_impact_factor(item="cpu", impact_type=impact_type)["die_impact"],
    )
    cpu_impact = Impact(
        value=get_impact_factor(item="cpu", impact_type=impact_type)["impact"],
        min=get_impact_factor(item="cpu", impact_type=impact_type)["impact"],
        max=get_impact_factor(item="cpu", impact_type=impact_type)["impact"],
    )

    impact = Impact(
        value=(cpu.die_size.value * cpu_die_impact.value + cpu_impact.value)
        * cpu.units.value,
        min=(cpu.die_size.min * cpu_die_impact.min + cpu_impact.min) * cpu.units.min,
        max=(cpu.die_size.max * cpu_die_impact.max + cpu_impact.max) * cpu.units.max,
    )

    impact.allocate(duration, cpu.usage.hours_life_time)

    return (
        impact.value,
        impact.min,
        impact.max,
        ["End of life is not included in the calculation"],
    )


def assembly_impact_embedded(
    impact_type: str, duration: int, model: ComponentAssembly
) -> ComputedImpacts:
    impact = Impact(
        value=get_impact_factor(item="assembly", impact_type=impact_type)["impact"]
        * model.units.value,
        min=get_impact_factor(item="assembly", impact_type=impact_type)["impact"]
        * model.units.min,
        max=get_impact_factor(item="assembly", impact_type=impact_type)["impact"]
        * model.units.max,
    )

    impact.allocate(duration, model.usage.hours_life_time)

    return (
        impact.value,
        impact.min,
        impact.max,
        ["End of life is not included in the calculation"],
    )


def casing_impact_embedded(
    impact_type: str, duration: int, case: ComponentCase
) -> ComputedImpacts:
    if case.case_type.value == "rack":
        computed_impact = impact_manufacture_rack(impact_type, case)
    elif case.case_type.value == "blade":
        computed_impact = impact_manufacture_blade(impact_type, case)
    else:
        computed_impact = impact_manufacture_rack(impact_type, case)

    impact = Impact(
        value=computed_impact[0] * case.units.value,
        min=computed_impact[1] * case.units.min,
        max=computed_impact[2] * case.units.max,
    )

    impact.allocate(duration, case.usage.hours_life_time)

    return (
        impact.value,
        impact.min,
        impact.max,
        ["End of life is not included in the calculation"],
    )


def impact_manufacture_rack(impact_type: str, case: ComponentCase) -> ComputedImpacts:
    impact_factor = Impact(
        value=get_impact_factor(item="case", impact_type=impact_type)["rack"]["impact"],
        min=get_impact_factor(item="case", impact_type=impact_type)["rack"]["impact"],
        max=get_impact_factor(item="case", impact_type=impact_type)["rack"]["impact"],
    )

    if case.case_type.is_archetype() and case.case_type.value == "rack":
        blade_impact = impact_manufacture_blade(impact_type, case)
        if blade_impact[0] > impact_factor.value:
            return (
                impact_factor.value,
                impact_factor.min,
                blade_impact[2],
                ["End of life is not included in the calculation"],
            )
        else:
            return (
                impact_factor.value,
                blade_impact[1],
                impact_factor.max,
                ["End of life is not included in the calculation"],
            )
    return (
        impact_factor.value * case.units.value,
        impact_factor.min * case.units.min,
        impact_factor.max * case.units.max,
        ["End of life is not included in the calculation"],
    )


def impact_manufacture_blade(impact_type: str, case: ComponentCase) -> ComputedImpacts:
    impact_blade_server, impact_blade_16_slots = get_impact_constants_blade(impact_type)

    impact = compute_impact_manufacture_blade(
        impact_blade_server, impact_blade_16_slots
    )

    if case.case_type.is_archetype() and case.case_type.value == "blade":
        rack_impact = impact_manufacture_rack(impact_type, case)
        if rack_impact[0] > impact.value:
            return (
                impact.value,
                impact.min,
                rack_impact[2],
                ["End of life is not included in the calculation"],
            )
        else:
            return (
                impact.value,
                rack_impact[1],
                impact.max,
                ["End of life is not included in the calculation"],
            )

    return (
        impact.value * case.units.value,
        impact.min * case.units.min,
        impact.max * case.units.max,
        ["End of life is not included in the calculation"],
    )


def get_impact_constants_blade(impact_type: str) -> Tuple[Impact, Impact]:
    impact_blade_server = Impact(
        value=get_impact_factor(item="case", impact_type=impact_type)["blade"][
            "impact_blade_server"
        ],
        min=get_impact_factor(item="case", impact_type=impact_type)["blade"][
            "impact_blade_server"
        ],
        max=get_impact_factor(item="case", impact_type=impact_type)["blade"][
            "impact_blade_server"
        ],
    )
    impact_blade_16_slots = Impact(
        value=get_impact_factor(item="case", impact_type=impact_type)["blade"][
            "impact_blade_16_slots"
        ],
        min=get_impact_factor(item="case", impact_type=impact_type)["blade"][
            "impact_blade_16_slots"
        ],
        max=get_impact_factor(item="case", impact_type=impact_type)["blade"][
            "impact_blade_16_slots"
        ],
    )

    return impact_blade_server, impact_blade_16_slots


def compute_impact_manufacture_blade(
    impact_blade_server: Impact, impact_blade_16_slots: Impact
) -> ImpactFactor:
    return ImpactFactor(
        value=(impact_blade_16_slots.value / 16) + impact_blade_server.value,
        min=(impact_blade_16_slots.min / 16) + impact_blade_server.min,
        max=(impact_blade_16_slots.max / 16) + impact_blade_server.max,
    )


def iot_functional_blocks_impact_embedded(
    impact_type: str, duration: int, function_blocks: ComponentFunctionalBlock
) -> ComputedImpacts:
    impact = Impact(
        value=get_iot_impact_factor(
            function_blocks.IMPACT_KEY, function_blocks.hsl_level.value, impact_type
        )
        * function_blocks.units.value,
        min=get_iot_impact_factor(
            function_blocks.IMPACT_KEY, function_blocks.hsl_level.value, impact_type
        )
        * function_blocks.units.min,
        max=get_iot_impact_factor(
            function_blocks.IMPACT_KEY, function_blocks.hsl_level.value, impact_type
        )
        * function_blocks.units.max,
    )

    impact.allocate(duration, function_blocks.usage.hours_life_time)

    return impact.value, impact.min, impact.max, []


def hdd_impact_embedded(
    impact_type: str, duration: int, hdd: ComponentHDD
) -> ComputedImpacts:
    impact = Impact(
        value=get_impact_factor(item="hdd", impact_type=impact_type)["impact"]
        * hdd.units.value,
        min=get_impact_factor(item="hdd", impact_type=impact_type)["impact"]
        * hdd.units.min,
        max=get_impact_factor(item="hdd", impact_type=impact_type)["impact"]
        * hdd.units.max,
    )

    impact.allocate(duration, hdd.usage.hours_life_time)

    return (
        impact.value,
        impact.min,
        impact.max,
        ["End of life is not included in the calculation"],
    )


def motherboard_impact_embedded(
    impact_type: str, duration: int, motherboard: ComponentMotherboard
) -> ComputedImpacts:
    impact = Impact(
        value=get_impact_factor(item="motherboard", impact_type=impact_type)["impact"]
        * motherboard.units.value,
        min=get_impact_factor(item="motherboard", impact_type=impact_type)["impact"]
        * motherboard.units.min,
        max=get_impact_factor(item="motherboard", impact_type=impact_type)["impact"]
        * motherboard.units.max,
    )

    impact.allocate(duration, motherboard.usage.hours_life_time)

    return (
        impact.value,
        impact.min,
        impact.max,
        ["End of life is not included in the calculation"],
    )


def power_supply_impact_embedded(
    impact_type: str, duration: int, power_supply: Component
) -> ComputedImpacts:
    if isinstance(power_supply, ComponentFunctionalBlock):
        return iot_functional_blocks_impact_embedded(
            impact_type, duration, power_supply
        )
    elif isinstance(power_supply, ComponentPowerSupply):
        return server_power_supply_impact_embedded(impact_type, duration, power_supply)
    else:
        raise NotImplementedError


def server_power_supply_impact_embedded(
    impact_type: str, duration: int, power_supply: ComponentPowerSupply
) -> ComputedImpacts:
    impact_factor = Impact(
        value=get_impact_factor(item="power_supply", impact_type=impact_type)["impact"],
        min=get_impact_factor(item="power_supply", impact_type=impact_type)["impact"],
        max=get_impact_factor(item="power_supply", impact_type=impact_type)["impact"],
    )

    impact = Impact(
        value=power_supply.unit_weight.value
        * impact_factor.value
        * power_supply.units.value,
        min=power_supply.unit_weight.min * impact_factor.min * power_supply.units.min,
        max=power_supply.unit_weight.max * impact_factor.max * power_supply.units.max,
    )

    impact.allocate(duration, power_supply.usage.hours_life_time)

    return (
        impact.value,
        impact.min,
        impact.max,
        ["End of life is not included in the calculation"],
    )


def ram_impact_embedded(
    impact_type: str, duration: int, ram: ComponentRAM
) -> ComputedImpacts:
    ram_die_impact = Impact(
        value=get_impact_factor(item="ram", impact_type=impact_type)["die_impact"],
        min=get_impact_factor(item="ram", impact_type=impact_type)["die_impact"],
        max=get_impact_factor(item="ram", impact_type=impact_type)["die_impact"],
    )

    ram_impact = Impact(
        value=get_impact_factor(item="ram", impact_type=impact_type)["impact"],
        min=get_impact_factor(item="ram", impact_type=impact_type)["impact"],
        max=get_impact_factor(item="ram", impact_type=impact_type)["impact"],
    )

    impact = Impact(
        value=(
            (ram.capacity.value / ram.density.value) * ram_die_impact.value
            + ram_impact.value
        )
        * ram.units.value,
        min=((ram.capacity.min / ram.density.max) * ram_die_impact.min + ram_impact.min)
        * ram.units.min,
        max=((ram.capacity.max / ram.density.min) * ram_die_impact.max + ram_impact.max)
        * ram.units.max,
    )

    impact.allocate(duration, ram.usage.hours_life_time)

    return (
        impact.value,
        impact.min,
        impact.max,
        ["End of life is not included in the calculation"],
    )


def ram_impact_use(
    impact_type: str, duration: int, ram: ComponentRAM
) -> ComputedImpacts:
    impact_factor = ram.usage.elec_factors[impact_type]

    if not ram.usage.avg_power.is_set():
        modeled_consumption = ram.model_power_consumption()
        ram.usage.avg_power.set_completed(
            modeled_consumption.value,
            min=modeled_consumption.min,
            max=modeled_consumption.max,
        )

    impact = ImpactFactor(
        value=impact_factor.value
        * (ram.usage.avg_power.value / 1000)
        * ram.usage.use_time_ratio.value
        * duration,
        min=impact_factor.min
        * (ram.usage.avg_power.min / 1000)
        * ram.usage.use_time_ratio.min
        * duration,
        max=impact_factor.max
        * (ram.usage.avg_power.max / 1000)
        * ram.usage.use_time_ratio.max
        * duration,
    )

    return impact.value, impact.min, impact.max, []


def ssd_impact_embedded(
    impact_type: str, duration: int, ssd: ComponentSSD
) -> ComputedImpacts:
    ssd_die_impact = Impact(
        value=get_impact_factor(item="ssd", impact_type=impact_type)["die_impact"],
        min=get_impact_factor(item="ssd", impact_type=impact_type)["die_impact"],
        max=get_impact_factor(item="ssd", impact_type=impact_type)["die_impact"],
    )
    ssd_impact = Impact(
        value=get_impact_factor(item="ssd", impact_type=impact_type)["impact"],
        min=get_impact_factor(item="ssd", impact_type=impact_type)["impact"],
        max=get_impact_factor(item="ssd", impact_type=impact_type)["impact"],
    )

    impact = Impact(
        value=(
            (ssd.capacity.value / ssd.density.value) * ssd_die_impact.value
            + ssd_impact.value
        )
        * ssd.units.value,
        min=((ssd.capacity.min / ssd.density.max) * ssd_die_impact.min + ssd_impact.min)
        * ssd.units.min,
        max=((ssd.capacity.max / ssd.density.min) * ssd_die_impact.max + ssd_impact.max)
        * ssd.units.max,
    )

    impact.allocate(duration, ssd.usage.hours_life_time)

    return (
        impact.value,
        impact.min,
        impact.max,
        ["End of life is not included in the calculation"],
    )


def iot_impact_embedded(
    impact_type: str, duration: int, iot_device: DeviceIoT
) -> ComputedImpacts:
    impacts = []
    min_impacts = []
    max_impacts = []
    warnings = iot_device.WARNINGS

    for component in iot_device.components:
        single_impact = compute_single_impact(
            component, "embedded", impact_type, duration
        )
        impacts.append(single_impact.value)
        min_impacts.append(single_impact.min)
        max_impacts.append(single_impact.max)
        warnings = warnings + single_impact.warnings

    return (
        sum(impacts) * iot_device.units.value,
        sum(min_impacts) * iot_device.units.min,
        sum(max_impacts) * iot_device.units.max,
        warnings,
    )


def iot_impact_use(
    impact_type: str, duration: int, iot_device: DeviceIoT
) -> ComputedImpacts:
    if iot_device.usage.avg_power.value is None:
        raise NotImplementedError

    impact_factor = iot_device.usage.elec_factors[impact_type]
    impact = (
        impact_factor.value
        * (iot_device.usage.avg_power.value / 1000)
        * iot_device.usage.use_time_ratio.value
        * duration
        * iot_device.units.value
    )
    min_impact = (
        impact_factor.min
        * (iot_device.usage.avg_power.min / 1000)
        * iot_device.usage.use_time_ratio.min
        * duration
        * iot_device.units.min
    )
    max_impact = (
        impact_factor.max
        * (iot_device.usage.avg_power.max / 1000)
        * iot_device.usage.use_time_ratio.max
        * duration
        * iot_device.units.max
    )

    return impact, min_impact, max_impact, []


def server_impact_embedded(
    impact_type: str, duration: int, server: DeviceServer
) -> ComputedImpacts:
    impacts = []
    min_impacts = []
    max_impacts = []
    warnings = []

    try:
        for component in server.components:
            component.usage.hours_life_time = server.usage.hours_life_time
            single_impact = compute_single_impact(
                component, "embedded", impact_type, duration
            )

            if single_impact is None:
                raise NotImplementedError

            impacts.append(single_impact.value)
            min_impacts.append(single_impact.min)
            max_impacts.append(single_impact.max)
            warnings = warnings + single_impact.warnings

        return sum(impacts), sum(min_impacts), sum(max_impacts), warnings

    except NotImplementedError:
        impact = Impact(
            value=get_impact_factor(item="SERVER", impact_type=impact_type)["impact"],
            min=get_impact_factor(item="SERVER", impact_type=impact_type)["impact"],
            max=get_impact_factor(item="SERVER", impact_type=impact_type)["impact"],
        )

        warnings = ["Generic data used for impact calculation."]

        impact.allocate(duration, server.usage.hours_life_time)

        return (
            impact.value * server.units.value,
            impact.min * server.units.min,
            impact.max * server.units.max,
            warnings,
        )


def server_impact_use(
    impact_type: str, duration: int, server: DeviceServer
) -> ComputedImpacts:
    impact_factor = server.usage.elec_factors[impact_type]

    if not server.usage.avg_power.is_set():
        modeled_consumption = server.model_power_consumption()
        server.usage.avg_power.set_completed(
            modeled_consumption.value,
            min=modeled_consumption.min,
            max=modeled_consumption.max,
        )

    # Compute impacts at component level
    compute_single_impact(server.cpu, USE, impact_type, duration)
    for ram in server.ram:
        compute_single_impact(ram, USE, impact_type, duration)

    impact = (
        impact_factor.value
        * (server.usage.avg_power.value / 1000)
        * server.usage.use_time_ratio.value
        * duration
        * server.units.value
    )
    min_impact = (
        impact_factor.min
        * (server.usage.avg_power.min / 1000)
        * server.usage.use_time_ratio.min
        * duration
        * server.units.min
    )
    max_impact = (
        impact_factor.max
        * (server.usage.avg_power.max / 1000)
        * server.usage.use_time_ratio.max
        * duration
        * server.units.max
    )

    return impact, min_impact, max_impact, []


def cloud_impact_embedded(
    impact_type: str, duration: int, cloud_instance: ServiceCloudInstance
) -> ComputedImpacts:
    impacts = []
    min_impacts = []
    max_impacts = []
    warnings = []
    default_allocation = (
        cloud_instance.vcpu.value / cloud_instance.platform.get_total_vcpu()
    )

    try:
        for component in cloud_instance.platform.components:
            component.usage.hours_life_time = (
                cloud_instance.platform.usage.hours_life_time
            )
            allocation = default_allocation
            if component.NAME == "RAM":
                allocation = (
                    cloud_instance.memory.value
                    / cloud_instance.platform.get_total_memory()
                )
            if component.NAME == "SSD":
                if cloud_instance.ssd_storage.has_value():
                    allocation = (
                        cloud_instance.ssd_storage.value
                        / cloud_instance.platform.get_total_disk_capacity("SSD")
                    )
                else:
                    continue
            if component.NAME == "HDD":
                if cloud_instance.hdd_storage.has_value():
                    allocation = (
                        cloud_instance.hdd_storage.value
                        / cloud_instance.platform.get_total_disk_capacity("HDD")
                    )
                else:
                    continue

            single_impact = compute_single_impact(
                component, "embedded", impact_type, duration, allocation
            )

            if single_impact is None:
                raise NotImplementedError

            impacts.append(single_impact.value)
            min_impacts.append(single_impact.min)
            max_impacts.append(single_impact.max)
            warnings = warnings + single_impact.warnings
        return sum(impacts), sum(min_impacts), sum(max_impacts), warnings

    except NotImplementedError:
        impact = Impact(
            value=get_impact_factor(item="SERVER", impact_type=impact_type)["impact"],
            min=get_impact_factor(item="SERVER", impact_type=impact_type)["impact"],
            max=get_impact_factor(item="SERVER", impact_type=impact_type)["impact"],
        )

        warnings = ["Generic data used for impact calculation."]

        impact.allocate(duration, cloud_instance.platform.usage.hours_life_time)

        return (
            impact.value * default_allocation,
            impact.min * default_allocation,
            impact.max * default_allocation,
            warnings,
        )


def cloud_impact_use(
    impact_type: str, duration: int, cloud_instance: ServiceCloudInstance
) -> ComputedImpacts:
    platform = cloud_instance.platform
    impact_factor = platform.usage.elec_factors[impact_type]

    if not cloud_instance.usage.avg_power.is_set():
        modeled_consumption = cloud_instance.model_power_consumption()
        cloud_instance.usage.avg_power.set_completed(
            value=modeled_consumption.value,
            min=modeled_consumption.min,
            max=modeled_consumption.max,
        )

    # Compute impacts at component level
    compute_single_impact(platform.cpu, USE, impact_type, duration)
    for ram in platform.ram:
        compute_single_impact(ram, USE, impact_type, duration)

    impact = (
        impact_factor.value
        * (cloud_instance.usage.avg_power.value / 1000)
        * platform.usage.use_time_ratio.value
        * duration
    )
    min_impact = (
        impact_factor.min
        * (cloud_instance.usage.avg_power.min / 1000)
        * platform.usage.use_time_ratio.min
        * duration
    )
    max_impact = (
        impact_factor.max
        * (cloud_instance.usage.avg_power.max / 1000)
        * platform.usage.use_time_ratio.max
        * duration
    )

    return impact, min_impact, max_impact, []


impacts_functions = {
    "CPU": {"use": cpu_impact_use, "embedded": cpu_impact_embedded},
    "RAM": {"use": ram_impact_use, "embedded": ram_impact_embedded},
    "SSD": {"use": simple_impact_use, "embedded": ssd_impact_embedded},
    "HDD": {"use": simple_impact_use, "embedded": hdd_impact_embedded},
    "POWER_SUPPLY": {
        "use": not_implemented_function,
        "embedded": power_supply_impact_embedded,
    },
    "ASSEMBLY": {"use": not_implemented_function, "embedded": assembly_impact_embedded},
    "CASE": {"use": simple_impact_use, "embedded": casing_impact_embedded},
    "MOTHERBOARD": {"use": simple_impact_use, "embedded": motherboard_impact_embedded},
    "SERVER": {"use": server_impact_use, "embedded": server_impact_embedded},
    "IOT_DEVICE": {"use": iot_impact_use, "embedded": iot_impact_embedded},
    "LAPTOP": {"use": simple_impact_use, "embedded": simple_embedded},
    "DESKTOP": {"use": simple_impact_use, "embedded": simple_embedded},
    "TABLET": {"use": simple_impact_use, "embedded": simple_embedded},
    "SMARTPHONE": {"use": simple_impact_use, "embedded": simple_embedded},
    "TELEVISION": {"use": simple_impact_use, "embedded": simple_embedded},
    "SMARTWATCH": {"use": simple_impact_use, "embedded": ram_impact_embedded},
    "BOX": {"use": simple_impact_use, "embedded": simple_embedded},
    "USB_STICK": {"use": simple_impact_use, "embedded": simple_embedded},
    "EXTERNAL_SSD": {"use": simple_impact_use, "embedded": simple_embedded},
    "EXTERNAL_HDD": {"use": simple_impact_use, "embedded": simple_embedded},
    "MONITOR": {"use": simple_impact_use, "embedded": simple_embedded},
    "CLOUD_INSTANCE": {"use": cloud_impact_use, "embedded": cloud_impact_embedded},
    "ACTUATORS": {
        "use": simple_impact_use,
        "embedded": iot_functional_blocks_impact_embedded,
    },
    "CASING": {
        "use": simple_impact_use,
        "embedded": iot_functional_blocks_impact_embedded,
    },
    "CONNECTIVITY": {
        "use": simple_impact_use,
        "embedded": iot_functional_blocks_impact_embedded,
    },
    "MEMORY": {
        "use": simple_impact_use,
        "embedded": iot_functional_blocks_impact_embedded,
    },
    "OTHERS": {
        "use": simple_impact_use,
        "embedded": iot_functional_blocks_impact_embedded,
    },
    "PCB": {
        "use": simple_impact_use,
        "embedded": iot_functional_blocks_impact_embedded,
    },
    "SECURITY": {
        "use": simple_impact_use,
        "embedded": iot_functional_blocks_impact_embedded,
    },
    "PROCESSING": {
        "use": simple_impact_use,
        "embedded": iot_functional_blocks_impact_embedded,
    },
    "SENSING": {
        "use": simple_impact_use,
        "embedded": iot_functional_blocks_impact_embedded,
    },
    "USER_INTERFACE": {
        "use": simple_impact_use,
        "embedded": iot_functional_blocks_impact_embedded,
    },
    "VR_CONTROLLER": {"use": simple_impact_use, "embedded": simple_embedded},
    "VR_HEADSET": {"use": simple_impact_use, "embedded": simple_embedded},
}
