"""SDK configuration with layered defaults."""

from __future__ import annotations

import os
import tomllib
from pathlib import Path
from typing import Any

DEFAULT_BASE_URL = "https://api.mostlyright.xyz"
DEFAULT_TIMEOUT = 30.0
DEFAULT_LIVE_TIMEOUT = 10.0
DEFAULT_MAX_RETRIES = 3

_BUILT_IN_DEFAULTS: dict[str, Any] = {
    "units": "raw",
    "tz": "UTC",
    "station": None,
    "base_url": DEFAULT_BASE_URL,
    "timeout": DEFAULT_TIMEOUT,
    "live_timeout": DEFAULT_LIVE_TIMEOUT,
    "max_retries": DEFAULT_MAX_RETRIES,
}

_ENV_MAP: dict[str, str] = {
    "THERMINAL_UNITS": "units",
    "THERMINAL_TZ": "tz",
    "THERMINAL_STATION": "station",
    "THERMINAL_BASE_URL": "base_url",
}


class TherminalConfig:
    """SDK configuration with layered defaults.

    Resolution order (last wins):
    1. Built-in defaults
    2. TOML file (~/.therminal.toml or $THERMINAL_CONFIG)
    3. Environment variables (THERMINAL_UNITS, THERMINAL_TZ, etc.)
    4. Constructor kwargs
    """

    __slots__ = (
        "units",
        "tz",
        "station",
        "base_url",
        "timeout",
        "live_timeout",
        "max_retries",
    )

    def __init__(
        self,
        *,
        path: str | Path | None = None,
        units: str | None = None,
        tz: str | None = None,
        station: str | None = None,
        base_url: str | None = None,
        timeout: float | None = None,
        live_timeout: float | None = None,
        max_retries: int | None = None,
    ) -> None:
        # Layer 1: built-in defaults
        merged = dict(_BUILT_IN_DEFAULTS)

        # Layer 2: TOML file
        toml_path = _resolve_config_path(path)
        if toml_path is not None and toml_path.exists():
            merged = _merge_toml(merged, toml_path)

        # Layer 3: environment variables
        merged = _merge_env(merged)

        # Layer 4: constructor kwargs (only non-None values override)
        kwargs: dict[str, Any] = {
            "units": units,
            "tz": tz,
            "station": station,
            "base_url": base_url,
            "timeout": timeout,
            "live_timeout": live_timeout,
            "max_retries": max_retries,
        }
        for key, val in kwargs.items():
            if val is not None:
                merged[key] = val

        # Set all attributes (using object.__setattr__ for __slots__ class)
        for key, val in merged.items():
            object.__setattr__(self, key, val)

    def resolve(
        self,
        *,
        units: str | None = None,
        tz: str | None = None,
        station: str | None = None,
    ) -> dict[str, str | None]:
        """Resolve per-call kwargs against config defaults.

        Returns a dict with resolved values, falling back to config
        defaults for any argument that is None.
        """
        return {
            "units": units if units is not None else self.units,
            "tz": tz if tz is not None else self.tz,
            "station": station if station is not None else self.station,
        }

    def __repr__(self) -> str:
        attrs = ", ".join(f"{k}={getattr(self, k)!r}" for k in self.__slots__)
        return f"TherminalConfig({attrs})"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, TherminalConfig):
            return NotImplemented
        return all(
            getattr(self, k) == getattr(other, k) for k in self.__slots__
        )


# ── Helpers ──────────────────────────────────────────────────


def _resolve_config_path(path: str | Path | None) -> Path | None:
    """Determine the TOML config file path.

    Priority: explicit path > $THERMINAL_CONFIG > ~/.therminal.toml.
    Returns None if no candidate is found.
    """
    if path is not None:
        return Path(path)

    env_path = os.environ.get("THERMINAL_CONFIG")
    if env_path:
        return Path(env_path)

    return Path.home() / ".therminal.toml"


def _merge_toml(merged: dict[str, Any], path: Path) -> dict[str, Any]:
    """Read a TOML config file and merge into *merged*.

    Expected sections:
      [defaults]  -> units, tz, station
      [api]       -> base_url, timeout
      [live]      -> timeout (maps to live_timeout), max_retries

    Returns a new dict (does not mutate the input).
    """
    result = dict(merged)

    try:
        raw = path.read_text(encoding="utf-8")
        data = tomllib.loads(raw)
    except tomllib.TOMLDecodeError as exc:
        raise ValueError(f"Invalid TOML config at {path}: {exc}") from exc

    defaults = data.get("defaults", {})
    for key in ("units", "tz", "station"):
        if key in defaults:
            result[key] = defaults[key]

    api = data.get("api", {})
    if "base_url" in api:
        result["base_url"] = api["base_url"]
    if "timeout" in api:
        result["timeout"] = float(api["timeout"])

    live = data.get("live", {})
    if "timeout" in live:
        result["live_timeout"] = float(live["timeout"])
    if "max_retries" in live:
        result["max_retries"] = int(live["max_retries"])

    return result


def _merge_env(merged: dict[str, Any]) -> dict[str, Any]:
    """Apply environment-variable overrides. Returns a new dict."""
    result = dict(merged)

    for env_var, config_key in _ENV_MAP.items():
        val = os.environ.get(env_var)
        if val is not None:
            result[config_key] = val

    return result
