from decimal import Decimal
import logging

from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import config_validation as cv, entity_registry
from homeassistant.helpers.event import TrackTemplate
from homeassistant.helpers.typing import ConfigType
import voluptuous as vol

from custom_components.powercalc.common import SourceEntity
from custom_components.powercalc.const import (
    CONF_CURRENT_ENTITY,
    CONF_POWER_FACTOR,
    CONF_VOLTAGE,
    OFF_STATES,
    UNAVAILABLE_STATES,
)
from custom_components.powercalc.errors import StrategyConfigurationError
from custom_components.powercalc.helpers import get_related_entity_by_device_class
from custom_components.powercalc.unit import evaluate_to_decimal

from .strategy_interface import PowerCalculationStrategyInterface

CONFIG_SCHEMA = vol.Schema(
    {
        vol.Required(CONF_VOLTAGE): vol.Coerce(float),
        vol.Optional(CONF_POWER_FACTOR, default=0.9): vol.Coerce(float),
        vol.Optional(CONF_CURRENT_ENTITY): cv.entity_id,
    },
)

_LOGGER = logging.getLogger(__name__)


class WledStrategy(PowerCalculationStrategyInterface):
    def __init__(
        self,
        config: ConfigType,
        light_entity: SourceEntity,
        hass: HomeAssistant,
        standby_power: float | None = None,
    ) -> None:
        self._hass = hass
        self._voltage = config.get(CONF_VOLTAGE) or 0
        self._power_factor = config.get(CONF_POWER_FACTOR) or 0.9
        self._light_entity = light_entity
        self._standby_power: Decimal = Decimal(standby_power or 0)
        self._configured_current_entity: str | None = config.get(CONF_CURRENT_ENTITY)
        self._estimated_current_entity: str | None = None

    async def calculate(self, entity_state: State) -> Decimal | None:
        light_state = (
            entity_state
            if entity_state.entity_id == self._light_entity.entity_id
            else self._hass.states.get(self._light_entity.entity_id)
        )
        if light_state is None:
            return None

        if light_state.state in OFF_STATES and self._standby_power:
            return self._standby_power

        current_state = (
            entity_state
            if entity_state.entity_id == self._estimated_current_entity
            else self._hass.states.get(self._estimated_current_entity)
        )
        if current_state is None:
            return None

        if current_state.state in UNAVAILABLE_STATES:
            _LOGGER.warning(
                "%s: Estimated current entity %s is not available",
                self._light_entity.entity_id,
                self._estimated_current_entity,
            )
            return None

        _LOGGER.debug(
            "%s: Estimated current %s (voltage=%d, power_factor=%.2f)",
            self._light_entity.entity_id,
            current_state.state,
            self._voltage,
            self._power_factor,
        )
        power = float(current_state.state) / 1000 * self._voltage * self._power_factor
        return evaluate_to_decimal(power)

    async def find_estimated_current_entity(self) -> str:
        if self._configured_current_entity:
            return self._configured_current_entity

        entity_reg = entity_registry.async_get(self._hass)
        entity_id = f"sensor.{self._light_entity.object_id}_estimated_current"
        entry = entity_reg.async_get(entity_id)
        if entry:
            return entry.entity_id

        if self._light_entity.entity_entry:
            entity = get_related_entity_by_device_class(self._hass, self._light_entity, SensorDeviceClass.CURRENT)
            if entity:
                return entity

        raise StrategyConfigurationError(
            "No estimated current entity found. Probably brightness limiter not enabled, "
            "or configured per output rather than globally. "
            "You can also point Powercalc to a current entity yourself using the current_entity option. "
            "See documentation",
        )

    def get_entities_to_track(self) -> list[str | TrackTemplate]:
        if self._estimated_current_entity:
            return [self._estimated_current_entity]
        return []  # pragma: no cover

    def can_calculate_standby(self) -> bool:
        return True

    async def validate_config(self) -> None:
        self._estimated_current_entity = await self.find_estimated_current_entity()
