"""Module for base energy module."""

from __future__ import annotations

from abc import ABC, abstractmethod
from enum import IntFlag, auto
from typing import TYPE_CHECKING, Any
from warnings import warn

from ..emeterstatus import EmeterStatus
from ..feature import Feature
from ..module import Module


class Energy(Module, ABC):
    """Base interface to represent an Energy module."""

    class ModuleFeature(IntFlag):
        """Features supported by the device."""

        #: Device reports :attr:`voltage` and :attr:`current`
        VOLTAGE_CURRENT = auto()
        #: Device reports :attr:`consumption_total`
        CONSUMPTION_TOTAL = auto()
        #: Device reports periodic stats via :meth:`get_daily_stats`
        #: and :meth:`get_monthly_stats`
        PERIODIC_STATS = auto()

    _supported: ModuleFeature = ModuleFeature(0)

    def supports(self, module_feature: ModuleFeature) -> bool:
        """Return True if module supports the feature."""
        return module_feature in self._supported

    def _initialize_features(self) -> None:
        """Initialize features."""
        device = self._device
        self._add_feature(
            Feature(
                device,
                name="Current consumption",
                attribute_getter="current_consumption",
                container=self,
                unit_getter=lambda: "W",
                id="current_consumption",
                precision_hint=1,
                category=Feature.Category.Primary,
                type=Feature.Type.Sensor,
            )
        )
        self._add_feature(
            Feature(
                device,
                name="Today's consumption",
                attribute_getter="consumption_today",
                container=self,
                unit_getter=lambda: "kWh",
                id="consumption_today",
                precision_hint=3,
                category=Feature.Category.Info,
                type=Feature.Type.Sensor,
            )
        )
        self._add_feature(
            Feature(
                device,
                id="consumption_this_month",
                name="This month's consumption",
                attribute_getter="consumption_this_month",
                container=self,
                unit_getter=lambda: "kWh",
                precision_hint=3,
                category=Feature.Category.Info,
                type=Feature.Type.Sensor,
            )
        )
        if self.supports(self.ModuleFeature.CONSUMPTION_TOTAL):
            self._add_feature(
                Feature(
                    device,
                    name="Total consumption since reboot",
                    attribute_getter="consumption_total",
                    container=self,
                    unit_getter=lambda: "kWh",
                    id="consumption_total",
                    precision_hint=3,
                    category=Feature.Category.Info,
                    type=Feature.Type.Sensor,
                )
            )
        if self.supports(self.ModuleFeature.VOLTAGE_CURRENT):
            self._add_feature(
                Feature(
                    device,
                    name="Voltage",
                    attribute_getter="voltage",
                    container=self,
                    unit_getter=lambda: "V",
                    id="voltage",
                    precision_hint=1,
                    category=Feature.Category.Primary,
                    type=Feature.Type.Sensor,
                )
            )
            self._add_feature(
                Feature(
                    device,
                    name="Current",
                    attribute_getter="current",
                    container=self,
                    unit_getter=lambda: "A",
                    id="current",
                    precision_hint=2,
                    category=Feature.Category.Primary,
                    type=Feature.Type.Sensor,
                )
            )

    @property
    @abstractmethod
    def status(self) -> EmeterStatus:
        """Return current energy readings."""

    @property
    @abstractmethod
    def current_consumption(self) -> float | None:
        """Get the current power consumption in Watt."""

    @property
    @abstractmethod
    def consumption_today(self) -> float | None:
        """Return today's energy consumption in kWh."""

    @property
    @abstractmethod
    def consumption_this_month(self) -> float | None:
        """Return this month's energy consumption in kWh."""

    @property
    @abstractmethod
    def consumption_total(self) -> float | None:
        """Return total consumption since last reboot in kWh."""

    @property
    @abstractmethod
    def current(self) -> float | None:
        """Return the current in A."""

    @property
    @abstractmethod
    def voltage(self) -> float | None:
        """Get the current voltage in V."""

    @abstractmethod
    async def get_status(self) -> EmeterStatus:
        """Return real-time statistics."""

    @abstractmethod
    async def erase_stats(self) -> dict:
        """Erase all stats."""

    @abstractmethod
    async def get_daily_stats(
        self, *, year: int | None = None, month: int | None = None, kwh: bool = True
    ) -> dict:
        """Return daily stats for the given year & month.

        The return value is a dictionary of {day: energy, ...}.
        """

    @abstractmethod
    async def get_monthly_stats(
        self, *, year: int | None = None, kwh: bool = True
    ) -> dict:
        """Return monthly stats for the given year."""

    _deprecated_attributes = {
        "emeter_today": "consumption_today",
        "emeter_this_month": "consumption_this_month",
        "realtime": "status",
        "get_realtime": "get_status",
        "erase_emeter_stats": "erase_stats",
        "get_daystat": "get_daily_stats",
        "get_monthstat": "get_monthly_stats",
    }

    if not TYPE_CHECKING:

        def __getattr__(self, name: str) -> Any:
            if attr := self._deprecated_attributes.get(name):
                msg = f"{name} is deprecated, use {attr} instead"
                warn(msg, DeprecationWarning, stacklevel=2)
                return getattr(self, attr)
            raise AttributeError(f"Energy module has no attribute {name!r}")