"""The Eheim Digital classicVARIO filter."""

from __future__ import annotations

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

from .device import EheimDigitalDevice
from .types import (
    ClassicVarioDataPacket,
    EheimDigitalDataMissingError,
    FilterErrorCode,
    FilterMode,
    MsgTitle,
    UsrDtaPacket,
)

if TYPE_CHECKING:
    from eheimdigital.hub import EheimDigitalHub

_LOGGER = getLogger(__package__)


class EheimDigitalClassicVario(EheimDigitalDevice):
    """Represent a Eheim Digital classicVARIO filter."""

    classic_vario_data: ClassicVarioDataPacket | None = None

    def __init__(self, hub: EheimDigitalHub, usrdta: UsrDtaPacket) -> None:
        """Initialize a classicVARIO filter."""
        super().__init__(hub, usrdta)

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

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

    async def set_classic_vario_param(self, data: dict[str, Any]) -> None:
        """Send a SET_CLASSIC_VARIO_PARAM packet, containing new values from data."""
        if self.classic_vario_data is None:
            _LOGGER.error(
                "set_classic_vario_param: No CLASSIC_VARIO_DATA packet received yet."
            )
            return
        await self.hub.send_packet({
            "title": "SET_CLASSIC_VARIO_PARAM",
            "to": self.classic_vario_data["from"],
            "filterActive": self.classic_vario_data["filterActive"],
            "rel_manual_motor_speed": self.classic_vario_data["rel_manual_motor_speed"],
            "rel_motor_speed_day": self.classic_vario_data["rel_motor_speed_day"],
            "rel_motor_speed_night": self.classic_vario_data["rel_motor_speed_night"],
            "startTime_day": self.classic_vario_data["startTime_day"],
            "startTime_night": self.classic_vario_data["startTime_night"],
            "pulse_motorSpeed_High": self.classic_vario_data["pulse_motorSpeed_High"],
            "pulse_motorSpeed_Low": self.classic_vario_data["pulse_motorSpeed_Low"],
            "pulse_Time_High": self.classic_vario_data["pulse_Time_High"],
            "pulse_Time_Low": self.classic_vario_data["pulse_Time_Low"],
            "pumpMode": self.classic_vario_data["pumpMode"],
            "from": "USER",
            **data,
        })

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

    async def set_active(self, *, active: bool) -> None:
        """Set whether the filter should be active or not."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({"filterActive": int(active)})

    @property
    def current_speed(self) -> int:
        """Return the current filter pump speed."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["rel_speed"]

    @property
    def manual_speed(self) -> int:
        """Return the manual filter pump speed."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["rel_manual_motor_speed"]

    async def set_manual_speed(self, speed: int) -> None:
        """Set the filter speed in manual mode."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({"rel_manual_motor_speed": speed})

    @property
    def day_speed(self) -> int:
        """Return the day filter pump speed in Bio mode."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["rel_motor_speed_day"]

    async def set_day_speed(self, speed: int) -> None:
        """Set the day filter speed in Bio mode."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({"rel_motor_speed_day": speed})

    @property
    def night_speed(self) -> int:
        """Return the night filter pump speed in Bio mode."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["rel_motor_speed_night"]

    async def set_night_speed(self, speed: int) -> None:
        """Set the night filter speed in Bio mode."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({"rel_motor_speed_night": speed})

    @property
    def day_start_time(self) -> time:
        """Return the day start time for Bio mode."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return time(
            self.classic_vario_data["startTime_day"] // 60,
            self.classic_vario_data["startTime_day"] % 60,
            tzinfo=timezone(timedelta(minutes=self.usrdta["timezone"])),
        )

    async def set_day_start_time(self, day_start_time: time) -> None:
        """Set the day start time for Bio mode."""
        if self.classic_vario_data is None:
            return
        t = (
            datetime(
                2020,
                1,
                1,
                day_start_time.hour,
                day_start_time.minute,
                tzinfo=day_start_time.tzinfo,
            )
            .astimezone(timezone(timedelta(minutes=self.usrdta["timezone"])))
            .time()
        )
        await self.set_classic_vario_param({"startTime_day": t.hour * 60 + t.minute})

    @property
    def night_start_time(self) -> time:
        """Return the night start time for Bio mode."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return time(
            self.classic_vario_data["startTime_night"] // 60,
            self.classic_vario_data["startTime_night"] % 60,
            tzinfo=timezone(timedelta(minutes=self.usrdta["timezone"])),
        )

    async def set_night_start_time(self, night_start_time: time) -> None:
        """Set the night start time for Bio mode."""
        if self.classic_vario_data is None:
            return
        t = (
            datetime(
                2020,
                1,
                1,
                night_start_time.hour,
                night_start_time.minute,
                tzinfo=night_start_time.tzinfo,
            )
            .astimezone(timezone(timedelta(minutes=self.usrdta["timezone"])))
            .time()
        )
        await self.set_classic_vario_param({"startTime_night": t.hour * 60 + t.minute})

    @property
    def high_pulse_speed(self) -> int:
        """Return pulse speed for high pulse in Pulse mode."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["pulse_motorSpeed_High"]

    async def set_high_pulse_speed(self, pulse_high: int) -> None:
        """Set pulse speed for high pulse in Pulse mode."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({"pulse_motorSpeed_High": pulse_high})

    @property
    def low_pulse_speed(self) -> int:
        """Return pulse speed for low pulse in Pulse mode."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["pulse_motorSpeed_Low"]

    async def set_low_pulse_speed(self, pulse_low: int) -> None:
        """Set pulse speed for low pulse in Pulse mode."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({"pulse_motorSpeed_Low": pulse_low})

    @property
    def pulse_speeds(self) -> tuple[int, int]:
        """Return pulse speeds for high and low pulse in Pulse mode."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return (
            self.classic_vario_data["pulse_motorSpeed_High"],
            self.classic_vario_data["pulse_motorSpeed_Low"],
        )

    async def set_pulse_speeds(self, pulse_high: int, pulse_low: int) -> None:
        """Set pulse speeds for high and low pulse in Pulse mode."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({
            "pulse_motorSpeed_High": pulse_high,
            "pulse_motorSpeed_Low": pulse_low,
        })

    @property
    def high_pulse_time(self) -> int:
        """Return pulse time for high pulse in Pulse mode."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["pulse_Time_High"]

    async def set_high_pulse_time(self, pulse_high: int) -> None:
        """Set pulse time for high pulse in Pulse mode."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({"pulse_Time_High": pulse_high})

    @property
    def low_pulse_time(self) -> int:
        """Return pulse time for low pulse in Pulse mode."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["pulse_Time_Low"]

    async def set_low_pulse_time(self, pulse_low: int) -> None:
        """Set pulse time for low pulse in Pulse mode."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({"pulse_Time_Low": pulse_low})

    @property
    def pulse_times(self) -> tuple[int, int]:
        """Return pulse times for high and low pulse in Pulse mode in seconds."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return (
            self.classic_vario_data["pulse_Time_High"],
            self.classic_vario_data["pulse_Time_Low"],
        )

    async def set_pulse_times(self, pulse_high: int, pulse_low: int) -> None:
        """Set pulse times in seconds for high and low pulse in Pulse mode."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({
            "pulse_Time_High": pulse_high,
            "pulse_Time_Low": pulse_low,
        })

    @property
    def service_hours(self) -> int:
        """Return the amount of hours until the next service is needed."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["serviceHour"]

    @property
    def filter_mode(self) -> FilterMode:
        """Return the current filter mode."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return FilterMode(self.classic_vario_data["pumpMode"])

    async def set_filter_mode(self, value: FilterMode) -> None:
        """Set the filter mode."""
        if self.classic_vario_data is None:
            return
        await self.set_classic_vario_param({"pumpMode": value.value})

    @property
    def turn_off_time(self) -> int:
        """Return the remaining turn off time in seconds."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["turnOffTime"]

    @property
    def turn_feeding_time(self) -> int:
        """Return the remaining pause time after the autofeeder sent a pause signal."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return self.classic_vario_data["turnTimeFeeding"]

    @property
    def error_code(self) -> FilterErrorCode:
        """Return the current error code of the filter."""
        if self.classic_vario_data is None:
            raise EheimDigitalDataMissingError
        return FilterErrorCode(self.classic_vario_data["errorCode"])

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

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