from typing import Any

import reactivex.operators as ops
from homeassistant.components.binary_sensor import (BinarySensorDeviceClass,
                                                    BinarySensorEntity)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import (DOMAIN, EcoFlowBaseEntity, EcoFlowEntity, HassioEcoFlowClient,
               select_bms)
from .ecoflow import is_delta, is_power_station, is_river


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback):
    client: HassioEcoFlowClient = hass.data[DOMAIN][entry.entry_id]
    entities = []

    if is_power_station(client.product):
        entities.extend([
            ChargingEntity(client),
            MainErrorEntity(client),
        ])
        if is_delta(client.product):
            entities.extend([
                ExtraErrorEntity(client, client.bms.pipe(select_bms(
                    1), ops.share()), "battery_error", "Extra1 status", 1),
                ExtraErrorEntity(client, client.bms.pipe(select_bms(
                    2), ops.share()), "battery_error", "Extra2 status", 2),
                InputEntity(client, client.inverter, "ac_in_type", "AC input"),
                InputEntity(client, client.mppt, "dc_in_state", "DC input"),
                CustomChargeEntity(client, client.inverter,
                                   "ac_in_limit_switch", "AC custom charge speed"),
            ])
        if is_river(client.product):
            entities.extend([
                ExtraErrorEntity(client, client.bms.pipe(select_bms(
                    1), ops.share()), "battery_error", "Extra status", 1),
                InputEntity(client, client.inverter, "in_type", "Input"),
            ])

    async_add_entities(entities)


class BaseEntity(BinarySensorEntity, EcoFlowEntity):
    def _on_updated(self, data: dict[str, Any]):
        self._attr_is_on = bool(data[self._key])


class ChargingEntity(BinarySensorEntity, EcoFlowBaseEntity):
    _attr_device_class = BinarySensorDeviceClass.BATTERY_CHARGING
    _battery_level = None
    _battery_level_max = None
    _in_power = None
    _out_power = None

    def __init__(self, client: HassioEcoFlowClient):
        super().__init__(client)
        self._attr_name = "Charging"
        self._attr_unique_id += "-in-charging"

    async def async_added_to_hass(self):
        await super().async_added_to_hass()
        self._subscribe(self._client.pd, self.__updated)
        self._subscribe(self._client.ems, self.__updated)

    def __updated(self, data: dict[str, Any]):
        self._attr_available = True
        self._on_updated(data)
        self.async_write_ha_state()

    def _on_updated(self, data: dict[str, Any]):
        if "in_power" in data:
            self._in_power = data["in_power"]
        if "out_power" in data:
            self._out_power = data["out_power"]
        if "battery_level" in data:
            self._battery_level = data["battery_level"]
        if "battery_level_max" in data:
            self._battery_level_max = data["battery_level_max"]

        if not self._in_power:
            self._attr_is_on = False
        elif (self._battery_level is not None) and (self._battery_level_max is not None) and (self._battery_level_max < self._battery_level):
            self._attr_is_on = False
        elif (self._in_power is not None) and (self._out_power is not None) and (self._in_power <= self._out_power):
            self._attr_is_on = False
        else:
            self._attr_is_on = True


class CustomChargeEntity(BaseEntity):
    _attr_entity_category = EntityCategory.CONFIG

    def _on_updated(self, data: dict[str, Any]):
        self._attr_is_on = data[self._key] == 2


class ExtraErrorEntity(BaseEntity):
    _attr_device_class = BinarySensorDeviceClass.PROBLEM

    def _on_updated(self, data: dict[str, Any]):
        self._attr_is_on = data[self._key] not in [0, 6]
        self._attr_extra_state_attributes = {"code": data[self._key]}


class MainErrorEntity(BinarySensorEntity, EcoFlowBaseEntity):
    _attr_device_class = BinarySensorDeviceClass.PROBLEM

    def __init__(self, client: HassioEcoFlowClient):
        super().__init__(client)
        self._attr_name = "Main status"
        self._attr_unique_id += "-error"
        self._attr_extra_state_attributes = {}

    @property
    def is_on(self):
        return next((True for x in self._attr_extra_state_attributes.values() if x not in [0, 6]), False)

    async def async_added_to_hass(self):
        await super().async_added_to_hass()
        self._subscribe(self._client.pd, self.__updated)
        self._subscribe(self._client.ems, self.__updated)
        self._subscribe(self._client.inverter, self.__updated)
        self._subscribe(self._client.mppt, self.__updated)

    def __updated(self, data: dict[str, Any]):
        self._attr_available = True
        if "ac_error" in data:
            self._attr_extra_state_attributes["ac"] = data["ac_error"]
        if "battery_main_error" in data:
            self._attr_extra_state_attributes["battery"] = data["battery_main_error"]
        if "dc_in_error" in data:
            self._attr_extra_state_attributes["dc"] = data["dc_in_error"]
        if "pd_error" in data:
            self._attr_extra_state_attributes["system"] = data["pd_error"]
        self.async_write_ha_state()


class InputEntity(BaseEntity):
    _attr_device_class = BinarySensorDeviceClass.POWER
