"""Client configuration for pybyd."""

from __future__ import annotations

import dataclasses
import os
from typing import Any


def _env_bool(value: str | None, default: bool) -> bool:
    if value is None:
        return default
    normalized = value.strip().lower()
    if normalized in {"1", "true", "yes", "y", "on"}:
        return True
    if normalized in {"0", "false", "no", "n", "off"}:
        return False
    return default


@dataclasses.dataclass(frozen=True)
class DeviceProfile:
    """Device identity fields sent with every request.

    These correspond to the outer payload fields that identify
    the mobile device to the BYD API.
    """

    ostype: str = "and"
    imei: str = "BANGCLE01234"
    mac: str = "00:00:00:00:00:00"
    model: str = "POCO F1"
    sdk: str = "35"
    mod: str = "Xiaomi"
    imei_md5: str = "00000000000000000000000000000000"
    mobile_brand: str = "XIAOMI"
    mobile_model: str = "POCO F1"
    device_type: str = "0"
    network_type: str = "wifi"
    os_type: str = "15"
    os_version: str = "35"


@dataclasses.dataclass(frozen=True)
class BydConfig:
    """Client configuration.

    Parameters
    ----------
    username : str
        BYD account email or phone number.
    password : str
        BYD account password.
    base_url : app
        API base URL. Defaults to the EU overseas endpoint.
    country_code : str
        ISO country code (e.g. ``"NL"``).
    language : str
        Language code (e.g. ``"en"``).
    time_zone : str
        IANA time zone string.
    app_version : str
        App version string sent to the API.
    app_inner_version : str
        Internal app version number.
    soft_type : str
        Software type identifier.
    tbox_version : str
        T-Box version for vehicle communication.
    is_auto : str
        Auto-login flag.
    control_pin : str or None
        6-digit remote control PIN set in the BYD app. Required for
        vehicle control commands (lock, unlock, climate, etc.).
        The PIN is hashed with MD5 before sending to the API.
    session_ttl : float
        Session token time-to-live in seconds.  After this interval
        the client will automatically re-authenticate on the next API
        call.  Defaults to 12 hours.  Set to ``0`` to disable
        automatic expiry (the session will only refresh on auth errors).
    mqtt_enabled : bool
        Enable MQTT background listener for realtime state enrichment.
    mqtt_keepalive : int
        MQTT keepalive in seconds.
    mqtt_command_timeout : float
        Seconds to wait for MQTT command result before HTTP polling fallback.
    api_trace_enabled : bool
        Enable transport-level request/response tracing callback.
    device : DeviceProfile
        Device identity fields.
    """

    username: str
    password: str
    base_url: str = "https://dilinkappoversea-eu.byd.auto"
    country_code: str = "NL"
    language: str = "en"
    time_zone: str = "Europe/Amsterdam"
    app_version: str = "3.2.2"
    app_inner_version: str = "322"
    soft_type: str = "0"
    tbox_version: str = "3"
    is_auto: str = "1"
    control_pin: str | None = None
    session_ttl: float = 12 * 3600
    mqtt_enabled: bool = True
    mqtt_keepalive: int = 120
    mqtt_command_timeout: float = 8.0
    api_trace_enabled: bool = False
    device: DeviceProfile = dataclasses.field(default_factory=DeviceProfile)

    @classmethod
    def from_env(cls, **overrides: Any) -> BydConfig:
        """Create configuration from environment variables.

        Reads ``BYD_USERNAME``, ``BYD_PASSWORD``, and optional ``BYD_*``
        variables matching the Node.js client's convention. Explicit
        keyword arguments override environment values.

        Parameters
        ----------
        **overrides
            Explicit field values that take precedence over env vars.

        Returns
        -------
        BydConfig
            Populated configuration.
        """
        env = os.environ

        device_kwargs: dict[str, str] = {}
        _ENV_DEVICE_MAP = {
            "BYD_OSTYPE": "ostype",
            "BYD_IMEI": "imei",
            "BYD_MAC": "mac",
            "BYD_MODEL": "model",
            "BYD_SDK": "sdk",
            "BYD_MOD": "mod",
            "BYD_IMEI_MD5": "imei_md5",
            "BYD_MOBILE_BRAND": "mobile_brand",
            "BYD_MOBILE_MODEL": "mobile_model",
            "BYD_DEVICE_TYPE": "device_type",
            "BYD_NETWORK_TYPE": "network_type",
            "BYD_OS_TYPE": "os_type",
            "BYD_OS_VERSION": "os_version",
        }
        for env_key, field_name in _ENV_DEVICE_MAP.items():
            val = env.get(env_key)
            if val is not None:
                device_kwargs[field_name] = val

        # Allow overriding device fields via a nested dict
        device_overrides = overrides.pop("device", None)
        if isinstance(device_overrides, dict):
            device_kwargs.update(device_overrides)
        elif isinstance(device_overrides, DeviceProfile):
            device_kwargs = dataclasses.asdict(device_overrides)

        device = DeviceProfile(**device_kwargs) if device_kwargs else DeviceProfile()

        _ENV_CONFIG_MAP = {
            "BYD_USERNAME": "username",
            "BYD_PASSWORD": "password",
            "BYD_COUNTRY_CODE": "country_code",
            "BYD_LANGUAGE": "language",
            "BYD_TIME_ZONE": "time_zone",
            "BYD_APP_VERSION": "app_version",
            "BYD_APP_INNER_VERSION": "app_inner_version",
            "BYD_SOFT_TYPE": "soft_type",
            "BYD_TBOX_VERSION": "tbox_version",
            "BYD_IS_AUTO": "is_auto",
            "BYD_CONTROL_PIN": "control_pin",
        }
        config_kwargs: dict[str, Any] = {"device": device}
        for env_key, field_name in _ENV_CONFIG_MAP.items():
            val = env.get(env_key)
            if val is not None:
                config_kwargs[field_name] = val

        # session_ttl is numeric, handle separately
        ttl_env = env.get("BYD_SESSION_TTL")
        if ttl_env is not None and "session_ttl" not in overrides:
            config_kwargs["session_ttl"] = float(ttl_env)

        if "mqtt_enabled" not in overrides:
            config_kwargs["mqtt_enabled"] = _env_bool(env.get("BYD_MQTT_ENABLED"), True)

        keepalive_env = env.get("BYD_MQTT_KEEPALIVE")
        if keepalive_env is not None and "mqtt_keepalive" not in overrides:
            config_kwargs["mqtt_keepalive"] = int(keepalive_env)

        timeout_env = env.get("BYD_MQTT_COMMAND_TIMEOUT")
        if timeout_env is not None and "mqtt_command_timeout" not in overrides:
            config_kwargs["mqtt_command_timeout"] = float(timeout_env)

        if "api_trace_enabled" not in overrides:
            config_kwargs["api_trace_enabled"] = _env_bool(
                env.get("BYD_API_TRACE_ENABLED"),
                False,
            )

        config_kwargs.update(overrides)

        return cls(**config_kwargs)
