from app.api.errors.iot import MissingIOTDataError
from app.models.domain.devices import ACATAHThermalModeSetRequest
from app.schemas.equipment import VAVBox
from app.services.transfer import Season


def count_vav_box_weight(
        realtime: float,
        target: float,
        upper_limit_flow: float,
        lower_limit_flow: float,
        current_flow_set: float,
        valve_opening: float,
        supply_air_temp: float,
) -> float:
    diff = realtime - target
    flag = False
    if current_flow_set < lower_limit_flow * 1.1 or valve_opening < 10.0:
        if supply_air_temp < realtime:
            if diff < 0:
                flag = True
        if supply_air_temp > realtime:
            if diff > 0:
                flag = True
    elif current_flow_set > upper_limit_flow * 0.9 or valve_opening > 90.0:
        if supply_air_temp > realtime:
            if diff > 0:
                flag = True
        if supply_air_temp < realtime:
            if diff < 0:
                flag = True

    if flag:
        if abs(diff) < 1:
            weight = 0.0
        else:
            weight = diff
        weight = max(-4.0, min(4.0, weight))
    else:
        weight = 0

    return weight


class ACATAHThermalModeController:
    """
    Decide whether to use cooling or heating mode according to space condition 
    controlled by VAV Box.
    Writen by WuXu
    """

    def __init__(self, vav_boxes_list: list[VAVBox], season: Season):
        super(ACATAHThermalModeController, self).__init__()
        self.vav_boxes_list = vav_boxes_list
        self.season = season

    def build(self) -> str:
        weight = 0.0
        for box in self.vav_boxes_list:
            try:
                weight += count_vav_box_weight(
                    box.virtual_realtime_temperature,
                    box.virtual_target_temperature,
                    box.supply_air_flow_upper_limit,
                    box.supply_air_flow_lower_limit,
                    box.supply_air_flow_set,
                    box.valve_opening,
                    box.supply_air_temperature
                )
            except TypeError:
                raise MissingIOTDataError

        if weight > 0:
            mode = "cooling"
        elif weight < 0:
            mode = "heating"
        else:
            mode = "hold"

        return mode


def build_acatah_thermal_mode_set(params: ACATAHThermalModeSetRequest) -> str:
    vav_list = list()
    for raw_vav in params.vav_list:
        if raw_vav.virtual_realtime_temperature and raw_vav.virtual_temperature_target:
            vav = VAVBox(
                virtual_realtime_temperature=raw_vav.virtual_realtime_temperature,
                virtual_target_temperature=raw_vav.virtual_temperature_target,
                supply_air_flow_lower_limit=raw_vav.supply_air_flow_lower_limit,
                supply_air_flow_upper_limit=raw_vav.supply_air_flow_upper_limit,
                supply_air_flow_set=raw_vav.supply_air_flow_set,
                valve_opening=raw_vav.valve_opening,
            )
            vav_list.append(vav)

    controller = ACATAHThermalModeController(vav_list, Season(params.season))
    mode = controller.build()

    return mode