from contextlib import suppress
from deprecated import deprecated

from PyViCare.PyViCareDevice import Device
from PyViCare.PyViCareUtils import (PyViCareNotSupportedFeatureError, handleAPICommandErrors, handleNotSupported)


class VentilationDevice(Device):
    """This is the base class for all ventilation devices.
    This class connects to the Viessmann ViCare API.
    The authentication is done through OAuth2.
    Note that currently, a new token is generated for each run.
    """

    @handleNotSupported
    def getWifiSignalStrength(self) -> int:
        return int(self.getProperty("tcu.wifi")["properties"]["strength"]["value"])

    @handleNotSupported
    def getVentilationDemand(self) -> str:
        return str(self.getProperty("ventilation.operating.state")["properties"]["demand"]["value"])

    @handleNotSupported
    def getVentilationReason(self) -> str:
        return str(self.getProperty("ventilation.operating.state")["properties"]["reason"]["value"])

    @handleNotSupported
    def getVentilationModes(self) -> list[str]:
        return list[str](self.getProperty("ventilation.operating.modes.active")["commands"]["setMode"]["params"]["mode"]["constraints"]["enum"])

    @handleNotSupported
    @deprecated(reason="renamed, use getVentilationModes", version="2.40.0")
    def getAvailableModes(self):
        return self.getVentilationModes()

    @handleNotSupported
    def getVentilationMode(self, mode: str) -> bool:
        return bool(self.getProperty(f"ventilation.operating.modes.{mode}")["properties"]["active"]["value"])

    @handleNotSupported
    def getActiveVentilationMode(self) -> str:
        return str(self.getProperty("ventilation.operating.modes.active")["properties"]["value"]["value"])

    @handleNotSupported
    def getVentilationLevels(self) -> list[str]:
        return list[str](self.getProperty("ventilation.operating.modes.permanent")["commands"]["setLevel"]["params"]["level"]["constraints"]["enum"])

    @handleNotSupported
    @deprecated(reason="renamed, use getVentilationLevels", version="2.40.0")
    def getPermanentLevels(self) -> list[str]:
        return list[str](self.getVentilationLevels())

    @handleNotSupported
    def getVentilationLevel(self) -> str:
        return str(self.getProperty("ventilation.operating.state")["properties"]["level"]["value"])

    @handleAPICommandErrors
    def setVentilationLevel(self, level: str):
        return self.setProperty("ventilation.operating.modes.permanent", "setLevel", {'level': level})

    @handleAPICommandErrors
    @deprecated(reason="renamed, use setVentilationLevel", version="2.40.0")
    def setPermanentLevel(self, level: str):
        return self.setVentilationLevel(level)

    @handleNotSupported
    @deprecated(reason="renamed, use getActiveVentilationMode", version="2.40.0")
    def getActiveMode(self):
        return self.getActiveVentilationMode()

    def activateVentilationMode(self, mode: str):
        """ Set the active ventilation mode
        Parameters
        ----------
        mode : str
            Valid mode can be obtained using getVentilationModes()

        Returns
        -------
        result: json
            json representation of the answer
        """
        return self.setProperty("ventilation.operating.modes.active", "setMode", {'mode': mode})

    @deprecated(reason="renamed, use activateVentilationMode", version="2.40.0")
    def setActiveMode(self, mode):
        """ Set the active mode
        Parameters
        ----------
        mode : str
            Valid mode can be obtained using getModes()

        Returns
        -------
        result: json
            json representation of the answer
        """
        return self.activateVentilationMode(mode)

    @handleNotSupported
    def getVentilationQuickmodes(self) -> list[str]:
        available_quickmodes = []
        for quickmode in ['comfort', 'eco', 'forcedLevelFour', 'holiday', 'standby', 'silent']:
            with suppress(PyViCareNotSupportedFeatureError):
                if self.getProperty(f"ventilation.quickmodes.{quickmode}") is not None:
                    available_quickmodes.append(quickmode)
        return available_quickmodes

    @handleNotSupported
    def getVentilationQuickmode(self, quickmode: str) -> bool:
        return bool(self.getProperty(f"ventilation.quickmodes.{quickmode}")["properties"]["active"]["value"])

    @handleNotSupported
    def activateVentilationQuickmode(self, quickmode: str) -> None:
        self.setProperty(f"ventilation.quickmodes.{quickmode}", "activate", {})

    @handleNotSupported
    def deactivateVentilationQuickmode(self, quickmode: str) -> None:
        self.setProperty(f"ventilation.quickmodes.{quickmode}", "deactivate", {})

    @handleNotSupported
    @deprecated(
        reason=(
            "ventilation.operating.programs.* was deprecated by Viessmann on 2024-09-15. "
            "Use getVentilationModes() instead."
        ),
        version="2.61.0",
    )
    def getVentilationPrograms(self):
        available_programs = []
        for program in ['basic', 'intensive', 'reduced', 'standard', 'standby', 'holidayAtHome', 'permanent']:
            with suppress(PyViCareNotSupportedFeatureError):
                if self.getProperty(f"ventilation.operating.programs.{program}") is not None:
                    available_programs.append(program)
        return available_programs

    @handleNotSupported
    @deprecated(reason="renamed, use getVentilationPrograms", version="2.40.0")
    def getAvailablePrograms(self):
        return self.getVentilationPrograms()

    @handleNotSupported
    @deprecated(
        reason=(
            "ventilation.operating.programs.* was deprecated by Viessmann on 2024-09-15. "
            "Use getVentilationLevel() to read the current fan level "
            "(from ventilation.operating.state) instead."
        ),
        version="2.61.0",
    )
    def getActiveVentilationProgram(self):
        return self.getProperty("ventilation.operating.programs.active")["properties"]["value"]["value"]

    @handleNotSupported
    @deprecated(reason="renamed, use getActiveVentilationProgram", version="2.40.0")
    def getActiveProgram(self):
        return self.getActiveVentilationProgram()

    @deprecated(
        reason=(
            "ventilation.operating.programs.* was deprecated by Viessmann on 2024-09-15. "
            "Use activateVentilationMode() and setVentilationLevel() instead."
        ),
        version="2.61.0",
    )
    def activateVentilationProgram(self, program):
        """ Activate a program
            NOTE
            DEVICE_COMMUNICATION_ERROR can just mean that the program is already on
        Parameters
        ----------
        program : str

        Returns
        -------
        result: json
            json representation of the answer
        """
        return self.setProperty(f"ventilation.operating.programs.{program}", "activate", {})

    @deprecated(reason="renamed, use activateVentilationProgram", version="2.40.0")
    def activateProgram(self, program):
        """ Activate a program
            NOTE
            DEVICE_COMMUNICATION_ERROR can just mean that the program is already on
        Parameters
        ----------
        program : str

        Returns
        -------
        result: json
            json representation of the answer
        """
        return self.activateVentilationProgram(program)

    @deprecated(
        reason=(
            "ventilation.operating.programs.* was deprecated by Viessmann on 2024-09-15. "
            "Use activateVentilationMode() with a different mode instead."
        ),
        version="2.61.0",
    )
    def deactivateVentilationProgram(self, program):
        """ Deactivate a program
        Parameters
        ----------
        program : str

        Returns
        -------
        result: json
            json representation of the answer
        """
        return self.setProperty(f"ventilation.operating.programs.{program}", "deactivate", {})

    @deprecated(reason="renamed, use deactivateVentilationProgram", version="2.40.0")
    def deactivateProgram(self, program):
        """ Deactivate a program
        Parameters
        ----------
        program : str

        Returns
        -------
        result: json
            json representation of the answer
        """
        return self.deactivateVentilationProgram(program)

    @handleNotSupported
    def getVentilationSchedule(self):
        properties = self.getProperty("ventilation.schedule")["properties"]
        return {
            "active": properties["active"]["value"],
            "mon": properties["entries"]["value"]["mon"],
            "tue": properties["entries"]["value"]["tue"],
            "wed": properties["entries"]["value"]["wed"],
            "thu": properties["entries"]["value"]["thu"],
            "fri": properties["entries"]["value"]["fri"],
            "sat": properties["entries"]["value"]["sat"],
            "sun": properties["entries"]["value"]["sun"]
        }

    @handleNotSupported
    @deprecated(reason="renamed, use getVentilationSchedule", version="2.40.0")
    def getSchedule(self):
        return self.getVentilationSchedule()

    @handleNotSupported
    def getOutsideTemperature(self) -> float:
        return float(self.getProperty("ventilation.sensors.temperature.outside")["properties"]["value"]["value"])

    @handleNotSupported
    def getOutsideHumidity(self) -> int:
        return int(self.getProperty("ventilation.sensors.humidity.outdoor")["properties"]["value"]["value"])

    @handleNotSupported
    def getSupplyTemperature(self) -> float:
        return float(self.getProperty("ventilation.sensors.temperature.supply")["properties"]["value"]["value"])

    @handleNotSupported
    def getExhaustTemperature(self) -> float:
        return float(self.getProperty("ventilation.sensors.temperature.exhaust")["properties"]["value"]["value"])

    @handleNotSupported
    def getExtractTemperature(self) -> float:
        return float(self.getProperty("ventilation.sensors.temperature.extract")["properties"]["value"]["value"])

    @handleNotSupported
    def getSupplyHumidity(self) -> int:
        return int(self.getProperty("ventilation.sensors.humidity.supply")["properties"]["value"]["value"])

    @handleNotSupported
    def getExhaustHumidity(self) -> int:
        return int(self.getProperty("ventilation.sensors.humidity.exhaust")["properties"]["value"]["value"])

    @handleNotSupported
    def getExtractHumidity(self) -> int:
        return int(self.getProperty("ventilation.sensors.humidity.extract")["properties"]["value"]["value"])

    @handleNotSupported
    def getVolatileOrganicCompounds(self) -> int:
        return int(self.getProperty("ventilation.sensors.volatileOrganicCompounds")["properties"]["value"]["value"])

    @handleNotSupported
    def getAirborneDustPM1(self) -> float:
        return float(self.getProperty("ventilation.sensors.airBorneDust.pm1")["properties"]["value"]["value"])

    @handleNotSupported
    def getAirborneDustPM2d5(self) -> float:
        return float(self.getProperty("ventilation.sensors.airBorneDust.pm2d5")["properties"]["value"]["value"])

    @handleNotSupported
    def getAirborneDustPM4(self) -> float:
        return float(self.getProperty("ventilation.sensors.airBorneDust.pm4")["properties"]["value"]["value"])

    @handleNotSupported
    def getAirborneDustPM10(self) -> float:
        return float(self.getProperty("ventilation.sensors.airBorneDust.pm10")["properties"]["value"]["value"])

    @handleNotSupported
    def getFilterHours(self) -> int:
        return int(self.getProperty("ventilation.filter.runtime")["properties"]["operatingHours"]["value"])

    @handleNotSupported
    def getFilterRemainingHours(self) -> int:
        return int(self.getProperty("ventilation.filter.runtime")["properties"]["remainingHours"]["value"])

    @handleNotSupported
    def getFilterOverdueHours(self) -> int:
        return int(self.getProperty("ventilation.filter.runtime")["properties"]["overdueHours"]["value"])

    @handleNotSupported
    def getSupplyFanHours(self) -> int:
        return int(self.getProperty("ventilation.fan.supply.runtime")["properties"]["value"]["value"])

    @handleNotSupported
    def getSupplyFanSpeed(self) -> int:
        return int(self.getProperty("ventilation.fan.supply")["properties"]["current"]["value"])

    @handleNotSupported
    def getSupplyFanTargetSpeed(self) -> int:
        return int(self.getProperty("ventilation.fan.supply")["properties"]["target"]["value"])

    @handleNotSupported
    def getHeatExchangerFrostProtectionActive(self) -> bool:
        return "off" != str(self.getProperty("ventilation.heatExchanger.frostprotection")["properties"]["status"]["value"])

    @handleNotSupported
    def getHeatRecoveryEfficiency(self) -> float:
        return float(self.getProperty("ventilation.heating.recovery")["properties"]["value"]["value"])

    @handleNotSupported
    def getSupplyVolumeFlow(self) -> int:
        return int(self.getProperty("ventilation.volumeFlow.current.input")["properties"]["value"]["value"])

    @handleNotSupported
    def getExhaustVolumeFlow(self) -> int:
        return int(self.getProperty("ventilation.volumeFlow.current.output")["properties"]["value"]["value"])

    @handleNotSupported
    def getConfiguredLevelOneVolumeFlow(self) -> int:
        return int(self.getProperty("ventilation.levels.levelOne")["properties"]["volumeFlow"]["value"])

    @handleNotSupported
    def getConfiguredLevelTwoVolumeFlow(self) -> int:
        return int(self.getProperty("ventilation.levels.levelTwo")["properties"]["volumeFlow"]["value"])

    @handleNotSupported
    def getConfiguredLevelThreeVolumeFlow(self) -> int:
        return int(self.getProperty("ventilation.levels.levelThree")["properties"]["volumeFlow"]["value"])

    @handleNotSupported
    def getConfiguredLevelFourVolumeFlow(self) -> int:
        return int(self.getProperty("ventilation.levels.levelFour")["properties"]["volumeFlow"]["value"])
