"""The EHEIM reeflexUV+e UV sterilizer."""

from __future__ import annotations

from datetime import time, timedelta, timezone
from functools import cached_property
from logging import getLogger
from typing import Any, override

from eheimdigital.device import EheimDigitalDevice
from eheimdigital.types import (
    EheimDigitalDataMissingError,
    MsgTitle,
    ReeflexDataPacket,
    ReeflexMode,
)

_LOGGER = getLogger(__package__)


class EheimDigitalReeflexUV(EheimDigitalDevice):
    """EHEIM reeflexUV+e UV sterilizer."""

    reeflex_data: ReeflexDataPacket | None = None

    @override
    async def parse_message(self, msg: dict[str, Any]) -> None:
        """Parse a message."""
        if msg["title"] == MsgTitle.REEFLEX_DATA:
            self.reeflex_data = ReeflexDataPacket(**msg)

    @override
    async def update(self) -> None:
        """Get the new reeflex state."""
        await self.hub.send_packet({
            "title": MsgTitle.GET_REEFLEX_UVC_DATA,
            "to": self.mac_address,
            "from": "USER",
        })

    async def set_reeflex_uvc_param(self, data: dict[str, Any]) -> None:
        """Send a SET_REEFLEX_UVC_PARAM packet, containing new values from data."""
        if self.reeflex_data is None:
            _LOGGER.error("set_reeflex_uvc_param: No REEFLEX_DATA packet received yet.")
            return
        await self.hub.send_packet({
            "title": MsgTitle.SET_REEFLEX_UVC_PARAM,
            "to": self.mac_address,
            "from": "USER",
            "startTime": self.reeflex_data["startTime"],
            "dailyBurnTime": self.reeflex_data["dailyBurnTime"],
            "isActive": self.reeflex_data["isActive"],
            "swOnDay": self.reeflex_data["swOnDay"],
            "swOnNight": self.reeflex_data["swOnNight"],
            "pause": self.reeflex_data["pause"],
            "booster": self.reeflex_data["booster"],
            "expert": self.reeflex_data["expert"],
            "mode": self.reeflex_data["mode"],
            "pauseTime": self.reeflex_data["pauseTime"],
            "boosterTime": self.reeflex_data["boosterTime"],
            "remainingPauseTime": self.reeflex_data["remainingPauseTime"],
            "remainingBoosterTime": self.reeflex_data["remainingBoosterTime"],
            "isUVCConnected": self.reeflex_data["isUVCConnected"],
            "timeUntilNextService": self.reeflex_data["timeUntilNextService"],
            "sync": self.reeflex_data["sync"],
            "partnerName": self.reeflex_data["partnerName"],
            **data,
        })

    @property
    def start_time(self) -> time:
        """Return the start time for daycycle mode."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return time(
            self.reeflex_data["startTime"] // 60,
            self.reeflex_data["startTime"] % 60,
            tzinfo=timezone(timedelta(minutes=self.usrdta["timezone"])),
        )

    async def set_day_start_time(self, time: time) -> None:
        """Set the day start time for Bio mode."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({
                "startTime": time.hour * 60 + time.minute
            })

    @property
    def daily_burn_time(self) -> int:
        """Return the daily burn time in minutes."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return self.reeflex_data["dailyBurnTime"]

    async def set_daily_burn_time(self, value: int) -> None:
        """Set the daily burn time of the lamp in minutes."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({"dailyBurnTime": value})

    @property
    def is_lighting(self) -> bool:
        """Return whether the lamp is currently turned on."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return bool(self.reeflex_data["isLighting"])

    @property
    def is_active(self) -> bool:
        """Return whether the device is active."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return bool(self.reeflex_data["isActive"])

    async def set_active(self, *, active: bool) -> None:
        """Set whether the device should be active or not."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({"isActive": int(active)})

    @property
    def sw_on_day(self) -> bool:
        """Unknown function."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return bool(self.reeflex_data["swOnDay"])

    async def set_sw_on_day(self, *, active: bool) -> None:
        """Unknown function."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({"swOnDay": int(active)})

    @property
    def sw_on_night(self) -> bool:
        """Unknown function."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return bool(self.reeflex_data["swOnDay"])

    async def set_sw_on_night(self, *, active: bool) -> None:
        """Unknown function."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({"swOnDay": int(active)})

    @property
    def pause(self) -> bool:
        """Return whether the device is paused."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return bool(self.reeflex_data["pause"])

    async def set_pause(self, *, pause: bool) -> None:
        """Set whether the device is paused."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({"pause": int(pause)})

    @property
    def booster(self) -> bool:
        """Return whether the booster is active."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return bool(self.reeflex_data["booster"])

    async def set_booster(self, *, active: bool) -> None:
        """Set whether the booster is active."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({"booster": int(active)})

    @property
    def booster_time(self) -> int:
        """Return the booster duration in minutes."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return self.reeflex_data["boosterTime"]

    async def set_booster_time(self, value: int) -> None:
        """Set the booster duration in minutes."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({"boosterTime": value})

    @property
    def remaining_booster_time(self) -> int:
        """Return the remaining booster time in minutes."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return self.reeflex_data["remainingBoosterTime"]

    @property
    def expert(self) -> bool:
        """Return whether expert mode is active."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return bool(self.reeflex_data["expert"])

    async def set_expert(self, *, active: bool) -> None:
        """Set whether expert mode is active."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({"expert": int(active)})

    @property
    def mode(self) -> ReeflexMode:
        """Return the operation mode."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return ReeflexMode(self.reeflex_data["mode"])

    async def set_mode(self, mode: ReeflexMode) -> None:
        """Set the operation mode."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({"mode": int(mode)})

    @property
    def pause_time(self) -> int:
        """Return the pause duration in minutes."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return self.reeflex_data["pauseTime"]

    async def set_pause_time(self, value: int) -> None:
        """Set the pause duration in minutes."""
        if self.reeflex_data is not None:
            await self.set_reeflex_uvc_param({"pauseTime": value})

    @property
    def remaining_pause_time(self) -> int:
        """Return the remaining pause time in minutes."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return self.reeflex_data["remainingPauseTime"]

    @property
    def is_uvc_connected(self) -> bool:
        """Return whether a UVC lamp is connected."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return bool(self.reeflex_data["isUVCConnected"])

    @property
    def time_until_next_service(self) -> int:
        """Return the time to the next service in hours."""
        if self.reeflex_data is None:
            raise EheimDigitalDataMissingError
        return self.reeflex_data["timeUntilNextService"]

    @cached_property
    @override
    def model_name(self) -> str | None:
        if self.reeflex_data is None:
            return super().model_name
        match self.reeflex_data["version"]:
            case 5:
                return "reeflexUV+e 500"
            case 8:
                return "reeflexUV+e 800"
            case 15:
                return "reeflexUV+e 1500"
            case 20:
                return "reeflexUV+e 2000"
            case _:
                return super().model_name

    @override
    def as_dict(self) -> dict[str, Any]:
        """Return the device as a dictionary."""
        return {
            "reeflex_data": self.reeflex_data,
            **super().as_dict(),
        }

    @property
    @override
    def is_missing_data(self) -> bool:
        return self.reeflex_data is None
