"""Asynchronous Python client for Powerfox."""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import UTC, datetime
from enum import IntEnum
from typing import Any, ClassVar

from mashumaro import field_options
from mashumaro.mixins.orjson import DataClassORJSONMixin


def _deserialize_timestamp(value: int | None) -> datetime | None:
    """Convert a timestamp to a datetime object."""
    if value is None:
        return None
    try:
        return datetime.fromtimestamp(value, tz=UTC)
    except (OverflowError, OSError, ValueError):
        return None


class DeviceType(IntEnum):
    """Enum for the different device types."""

    NO_TYPE = -1
    POWER_METER = 0
    COLD_WATER_METER = 1
    HOT_WATER_METER = 2
    HEAT_METER = 3
    GAS_METER = 4
    COLD_HOT_WATER_METER = 5

    @property
    def human_readable(self) -> str:
        """Return a human readable string for the device type."""
        return {
            DeviceType.POWER_METER: "Power Meter",
            DeviceType.COLD_WATER_METER: "Cold Water Meter",
            DeviceType.HOT_WATER_METER: "Hot Water Meter",
            DeviceType.HEAT_METER: "Heat Meter",
            DeviceType.GAS_METER: "Gas Meter",
            DeviceType.COLD_HOT_WATER_METER: "Cold/Hot Water Meter",
        }.get(self, "Unknown")


@dataclass
class Device(DataClassORJSONMixin):
    """Object representing a Device from Powerfox."""

    id: str = field(metadata=field_options(alias="DeviceId"))
    date_added: datetime = field(
        metadata=field_options(
            alias="AccountAssociatedSince",
            deserialize=lambda x: datetime.fromtimestamp(x, tz=UTC),
        )
    )
    main_device: bool = field(metadata=field_options(alias="MainDevice"))
    bidirectional: bool = field(metadata=field_options(alias="Prosumer"))
    type: DeviceType = field(metadata=field_options(alias="Division"))
    name: str = field(metadata=field_options(alias="Name"), default="Poweropti")


@dataclass
class Poweropti(DataClassORJSONMixin):
    """Object representing a Poweropti device."""

    outdated: bool = field(metadata=field_options(alias="Outdated"))
    timestamp: datetime = field(
        metadata=field_options(
            alias="Timestamp",
            deserialize=lambda x: datetime.fromtimestamp(x, tz=UTC),
        )
    )


@dataclass
class PowerMeter(Poweropti):
    """Object representing a Power device."""

    power: int = field(metadata=field_options(alias="Watt"))
    energy_usage: float | None = field(
        metadata=field_options(
            alias="A_Plus",
            deserialize=lambda x: x if x != 0 else None,
        ),
    )
    energy_return: float | None = field(
        metadata=field_options(
            alias="A_Minus",
            deserialize=lambda x: x if x != 0 else None,
        ),
    )
    energy_usage_high_tariff: float | None = field(
        metadata=field_options(alias="A_Plus_HT"), default=None
    )
    energy_usage_low_tariff: float | None = field(
        metadata=field_options(alias="A_Plus_NT"), default=None
    )


@dataclass
class HeatMeter(Poweropti):
    """Object representing a Heat device."""

    total_energy: int = field(metadata=field_options(alias="KiloWattHour"))
    delta_energy: int = field(metadata=field_options(alias="DeltaKiloWattHour"))
    total_volume: float = field(metadata=field_options(alias="CubicMeter"))
    delta_volume: float = field(metadata=field_options(alias="DeltaCubicMeter"))


@dataclass
class WaterMeter(Poweropti):
    """Object representing a Water device."""

    cold_water: float = field(metadata=field_options(alias="CubicMeterCold"))
    warm_water: float = field(metadata=field_options(alias="CubicMeterWarm"))


@dataclass
class ReportValue(DataClassORJSONMixin):
    """Object representing a report value entry."""

    device_id: str = field(metadata=field_options(alias="DeviceId"))
    timestamp: datetime = field(
        metadata=field_options(
            alias="Timestamp",
            deserialize=lambda x: datetime.fromtimestamp(x, tz=UTC),
        )
    )
    complete: bool = field(metadata=field_options(alias="Complete"))
    values_type: int | None = field(
        metadata=field_options(alias="ValuesType"),
        default=None,
    )
    delta: float | None = field(
        metadata=field_options(alias="Delta"),
        default=None,
    )
    delta_ht: float | None = field(
        metadata=field_options(alias="DeltaHT"),
        default=None,
    )
    delta_nt: float | None = field(
        metadata=field_options(alias="DeltaNT"),
        default=None,
    )
    delta_currency: float | None = field(
        metadata=field_options(alias="DeltaCurrency"),
        default=None,
    )
    total_delta: float | None = field(
        metadata=field_options(alias="TotalDelta"),
        default=None,
    )
    total_delta_currency: float | None = field(
        metadata=field_options(alias="TotalDeltaCurrency"),
        default=None,
    )
    consumption: float | None = field(
        metadata=field_options(alias="Consumption"),
        default=None,
    )
    consumption_kwh: float | None = field(
        metadata=field_options(alias="ConsumptionKWh"),
        default=None,
    )
    current_consumption: float | None = field(
        metadata=field_options(alias="CurrentConsumption"),
        default=None,
    )
    current_consumption_kwh: float | None = field(
        metadata=field_options(alias="CurrentConsumptionKwh"),
        default=None,
    )


@dataclass
class GasReport(DataClassORJSONMixin):
    """Object representing a gas report."""

    total_delta: float = field(metadata=field_options(alias="TotalDelta"))
    sum: float = field(metadata=field_options(alias="Sum"))
    total_delta_currency: float | None = field(
        metadata=field_options(alias="TotalDeltaCurrency"),
        default=None,
    )
    current_consumption_kwh: float | None = field(
        metadata=field_options(alias="CurrentConsumptionKwh"),
        default=None,
    )
    current_consumption: float | None = field(
        metadata=field_options(alias="CurrentConsumption"),
        default=None,
    )
    consumption_kwh: float | None = field(
        metadata=field_options(alias="ConsumptionKWh"),
        default=None,
    )
    consumption: float | None = field(
        metadata=field_options(alias="Consumption"),
        default=None,
    )
    max: float | None = field(
        metadata=field_options(alias="Max"),
        default=None,
    )
    max_currency: float | None = field(
        metadata=field_options(alias="MaxCurrency"),
        default=None,
    )
    max_consumption: float | None = field(
        metadata=field_options(alias="MaxConsumption"),
        default=None,
    )
    max_consumption_kwh: float | None = field(
        metadata=field_options(alias="MaxConsumptionKWh"),
        default=None,
    )
    min: float | None = field(
        metadata=field_options(alias="Min"),
        default=None,
    )
    min_consumption: float | None = field(
        metadata=field_options(alias="MinConsumption"),
        default=None,
    )
    min_consumption_kwh: float | None = field(
        metadata=field_options(alias="MinConsumptionKWh"),
        default=None,
    )
    avg_delta: float | None = field(
        metadata=field_options(alias="AvgDelta"),
        default=None,
    )
    avg_consumption: float | None = field(
        metadata=field_options(alias="AvgConsumption"),
        default=None,
    )
    avg_consumption_kwh: float | None = field(
        metadata=field_options(alias="AvgConsumptionKWh"),
        default=None,
    )
    meter_readings: list[dict[str, Any]] = field(
        default_factory=list,
        metadata=field_options(alias="MeterReadings"),
    )
    report_values: list[ReportValue] = field(
        default_factory=list,
        metadata=field_options(alias="ReportValues"),
    )
    sum_currency: float | None = field(
        metadata=field_options(alias="SumCurrency"),
        default=None,
    )


@dataclass
class EnergyReport(DataClassORJSONMixin):
    """Object representing an energy report section."""

    start_time: datetime | None = field(
        metadata=field_options(
            alias="StartTime",
            deserialize=_deserialize_timestamp,
        ),
        default=None,
    )
    start_time_currency: datetime | None = field(
        metadata=field_options(
            alias="StartTimeCurrency",
            deserialize=_deserialize_timestamp,
        ),
        default=None,
    )
    sum: float | None = field(
        metadata=field_options(alias="Sum"),
        default=None,
    )
    max: float | None = field(
        metadata=field_options(alias="Max"),
        default=None,
    )
    max_currency: float | None = field(
        metadata=field_options(alias="MaxCurrency"),
        default=None,
    )
    meter_readings: list[dict[str, Any]] = field(
        default_factory=list,
        metadata=field_options(alias="MeterReadings"),
    )
    report_values: list[ReportValue] = field(
        default_factory=list,
        metadata=field_options(alias="ReportValues"),
    )
    sum_currency: float | None = field(
        metadata=field_options(alias="SumCurrency"),
        default=None,
    )


@dataclass
class DeviceReport(DataClassORJSONMixin):
    """Object representing a report response."""

    gas: GasReport | None = field(
        default=None,
        metadata=field_options(alias="Gas"),
    )
    consumption: EnergyReport | None = field(
        default=None,
        metadata=field_options(alias="Consumption"),
    )
    feed_in: EnergyReport | None = field(
        default=None,
        metadata=field_options(alias="FeedIn"),
    )


@dataclass
class LocalResponse(DataClassORJSONMixin):
    """Object representing the local interface response.

    Maps OBIS codes from the local poweropti API to flat fields.
    Units: power in W, energy values in Wh.
    """

    _OBIS_MAP: ClassVar[dict[str, str]] = {
        "1.7.0": "power",
        "1.8.0": "energy_usage",
        "1.8.1": "energy_usage_high_tariff",
        "1.8.2": "energy_usage_low_tariff",
        "2.8.0": "energy_return",
    }

    timestamp: datetime = field(
        metadata=field_options(
            deserialize=lambda x: datetime.fromtimestamp(x, tz=UTC),
        )
    )
    power: int | None = None
    energy_usage: int | None = None
    energy_usage_high_tariff: int | None = None
    energy_usage_low_tariff: int | None = None
    energy_return: int | None = None

    @classmethod
    def __pre_deserialize__(cls, d: dict[str, Any]) -> dict[str, Any]:
        """Map OBIS values array to flat fields before deserialization."""
        for item in d.get("values", []):
            field_name = cls._OBIS_MAP.get(item["obis"])
            if field_name:
                d[field_name] = item["value"]
        return d
