"""Asynchronous Python client for Elgato Lights."""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import IntEnum

from mashumaro import field_options
from mashumaro.config import BaseConfig
from mashumaro.mixins.orjson import DataClassORJSONMixin
from mashumaro.types import SerializationStrategy


class IntegerIsBoolean(SerializationStrategy):
    """Boolean serialization strategy for integers."""

    def serialize(self, value: bool) -> int:  # noqa: FBT001
        """Serialize a boolean to an integer."""
        return int(value)

    def deserialize(self, value: int) -> bool:
        """Deserialize an integer to a boolean."""
        return bool(value)


class BaseModel(DataClassORJSONMixin):
    """Base model for all Elgato models."""

    # pylint: disable-next=too-few-public-methods
    class Config(BaseConfig):
        """Mashumaro configuration."""

        omit_none = True
        serialization_strategy = {bool: IntegerIsBoolean()}  # noqa: RUF012
        serialize_by_alias = True


@dataclass
class EnergySavingAdjustBrightnessSettings(BaseModel):
    """Object holding the Elgato Light battery energy saving settings for brightness.

    This object holds information about the Elgato Light energy saving settings
    for saving battery when using the light regarding brightness limits.

    Only applies to Elgato devices with a battery of course,
    like the Key Light Mini.

    Attributes
    ----------
        brightness: Adjusted brightness when energy saving is active.
        enabled: boolean

    """

    brightness: int
    enabled: bool = field(metadata=field_options(alias="enable"))


@dataclass
class EnergySavingSettings(BaseModel):
    """Object holding the Elgato Light battery energy saving settings.

    This object holds information about the Elgato Light energy saving settings
    for saving battery when using the light.

    Only applies to Elgato devices with a battery of course,
    like the Key Light Mini.

    Attributes
    ----------
        adjust_brightness: Adjust brightness when energy saving is active.
        disable_wifi: Disable Wi-Fi when energy saving is active.
        enabled: boolean
        minimum_battery_level: Use energy saving when battery level is below this value.

    """

    adjust_brightness: EnergySavingAdjustBrightnessSettings = field(
        metadata=field_options(alias="adjustBrightness")
    )
    disable_wifi: bool = field(metadata=field_options(alias="disableWifi"))
    enabled: bool = field(metadata=field_options(alias="enable"))
    minimum_battery_level: int = field(
        metadata=field_options(alias="minimumBatteryLevel")
    )


@dataclass
class BatterySettings(BaseModel):
    """Object holding the Elgato Light battery information.

    This object holds information about the Elgato Light battery.

    Only applies to Elgato devices with a battery of course,
    like the Key Light Mini.

    Attributes
    ----------
        energy_saving: Energy saving settings.
        bypass: If the battery is bypassed (studio mode).

    """

    energy_saving: EnergySavingSettings = field(
        metadata=field_options(alias="energySaving")
    )
    bypass: bool


@dataclass
class Wifi(BaseModel):
    """Object holding the Elgato device Wi-Fi information.

    This object holds wireless information about the Elgato device.

    Attributes
    ----------
        frequency: The frequency in MHz of the Wi-Fi network connected.
        rssi: The signal strength in dBm of the Wi-Fi network connected.
        signal_strength: The signal strength as a percentage.
        ssid: The SSID of the Wi-Fi network the device is connected to.

    """

    frequency: int = field(metadata=field_options(alias="frequencyMHz"))
    rssi: int
    ssid: str

    @property
    def signal_strength(self) -> int:
        """Convert RSSI to signal strength percentage (0-100)."""
        if self.rssi <= -100:
            return 0
        if self.rssi >= -50:
            return 100
        return 2 * (self.rssi + 100)


class PowerSource(IntEnum):
    """Enum for the power source of the Elgato Light."""

    UNKNOWN = 0
    MAINS = 1
    BATTERY = 2


class BatteryStatus(IntEnum):
    """Enum for the battery status of the Elgato Light.

    Value "1" seems to be unused. I could not get it to show up, no
    matter if the device was charging or not, in saving mode or even bypass.
    """

    DRAINING = 0
    CHARGING = 2
    CHARGED = 3


@dataclass
class BatteryInfo(BaseModel):
    """Object holding the Elgato Light device information.

    This object holds information about the Elgato Light.

    Attributes
    ----------
        charge_current: The charge current in A.
        charge_power: The charge power in W.
        charge_voltage: The charge voltage in V.
        input_charge_current: The charge current in mA.
        input_charge_power: The charge power in mW.
        input_charge_voltage: The charge voltage in mV.
        level: The battery level of the device in %.
        power_source: The power source of the device.
        status: The battery status.
        voltage: The current battery voltage in mV.

    """

    power_source: PowerSource = field(metadata=field_options(alias="powerSource"))
    level: float
    status: BatteryStatus
    voltage: int = field(metadata=field_options(alias="currentBatteryVoltage"))
    input_charge_voltage: int = field(
        metadata=field_options(alias="inputChargeVoltage")
    )
    input_charge_current: int = field(
        metadata=field_options(alias="inputChargeCurrent")
    )

    @property
    def input_charge_power(self) -> int:
        """Return the input charge power in mW."""
        return round(self.input_charge_voltage * self.input_charge_current / 1_000)

    @property
    def charge_current(self) -> float:
        """Return the charge current in A."""
        return round(self.input_charge_current / 1_000, 2)

    @property
    def charge_power(self) -> float:
        """Return the charge power in W."""
        return round(self.input_charge_power / 1_000, 2)

    @property
    def charge_voltage(self) -> float:
        """Return the charge voltage in V."""
        return round(self.input_charge_voltage / 1_000, 2)


@dataclass
# pylint: disable-next=too-many-instance-attributes
class Info(BaseModel):
    """Object holding the Elgato Light device information.

    This object holds information about the Elgato Light.

    Attributes
    ----------
        display_name: Configured display name of the Elgato Light.
        features: List of features this devices exposes.
        firmware_build_number: An integer with the build number of the firmware.
        firmware_version: String containing the firmware version.
        hardware_board_type: An integer indicating the board revision.
        hardware_revision: The revision of the board, if the device reports one.
        product_name: The product name.
        serial_number: Serial number of the Elgato Light.

    """

    features: list[str]
    firmware_build_number: int = field(
        metadata=field_options(alias="firmwareBuildNumber")
    )
    firmware_version: str = field(metadata=field_options(alias="firmwareVersion"))
    hardware_board_type: int = field(metadata=field_options(alias="hardwareBoardType"))
    product_name: str = field(metadata=field_options(alias="productName"))
    serial_number: str = field(metadata=field_options(alias="serialNumber"))
    display_name: str = field(
        default="Elgato Light", metadata=field_options(alias="displayName")
    )
    # Devices are not consistent about this one. A Key Light sends the string
    # "1", a Ring Light sends the number 0.2. It identifies a revision, it is
    # not something to do arithmetic with, so it is kept as text.
    hardware_revision: str | None = field(
        default=None, metadata=field_options(alias="hardwareRevision")
    )
    mac_address: str | None = field(
        default=None, metadata=field_options(alias="macAddress")
    )
    wifi: Wifi | None = field(default=None, metadata=field_options(alias="wifi-info"))


class PowerOnBehavior(IntEnum):
    """Enum for the power on behavior of the Elgato Light."""

    UNKNOWN = 0
    RESTORE_LAST = 1
    USE_DEFAULTS = 2


@dataclass
# pylint: disable-next=too-many-instance-attributes
class Settings(BaseModel):
    """Object holding the Elgato Light device settings.

    This object holds information about the Elgato Light settings.

    Attributes
    ----------
        battery: Battery settings, if the device has a battery.
        color_change_duration: Transition time of color changes in milliseconds.
        power_on_behavior: 1 = Restore last, 2 = Use defaults.
        power_on_brightness: The brightness used as default.
        power_on_hue: The hue value used as default.
        power_on_saturation: The saturation level used as default.
        power_on_temperature: The temperature level used as default.
        switch_off_duration: Turn off transition time in milliseconds.
        switch_on_duration: Turn on transition time in milliseconds.

    """

    color_change_duration: int = field(
        metadata=field_options(alias="colorChangeDurationMs")
    )
    power_on_behavior: PowerOnBehavior = field(
        metadata=field_options(alias="powerOnBehavior")
    )
    power_on_brightness: int = field(metadata=field_options(alias="powerOnBrightness"))
    switch_off_duration: int = field(
        metadata=field_options(alias="switchOffDurationMs")
    )
    switch_on_duration: int = field(metadata=field_options(alias="switchOnDurationMs"))
    battery: BatterySettings | None = None
    power_on_hue: float | None = field(
        default=None, metadata=field_options(alias="powerOnHue")
    )
    power_on_saturation: float | None = field(
        default=None, metadata=field_options(alias="powerOnSaturation")
    )
    power_on_temperature: int | None = field(
        default=None, metadata=field_options(alias="powerOnTemperature")
    )


@dataclass
class State(BaseModel):
    """Object holding the Elgato Light state.

    Represents a visible state of an Elgato Light.

    Attributes
    ----------
        on: A boolean indicating if the light is on or off.
        brightness: An integer between 0 and 100, representing the brightness.
        hue: The hue range as a float from 0 to 360 degrees.
        saturation: The color saturation as a float from 0 to 100.
        temperature: An integer representing the color temperature in mireds.

    """

    on: bool
    brightness: int
    hue: float | None = field(default=None)
    saturation: float | None = field(default=None)
    temperature: int | None = field(default=None)
