"""VoxCast Server configuration.

Sources, highest priority first:
  1. Environment variables  (VOXCAST_SERVER_*)
  2. server.toml            (path from VOXCAST_SERVER_CONFIG, else platform default)
  3. Field defaults

The API key is persisted back into server.toml on first run (see auth.py).
"""
from __future__ import annotations

import os
import sys
from pathlib import Path

from pydantic import field_validator
from pydantic_settings import (
    BaseSettings,
    PydanticBaseSettingsSource,
    SettingsConfigDict,
    TomlConfigSettingsSource,
)

VALID_MODELS = {"auto", "base", "small", "medium", "large-v3-turbo"}
VALID_DEVICES = {"auto", "cuda", "cpu"}


def config_file_path() -> Path:
    """server.toml location: explicit env, else next to the exe (Windows /
    frozen), else /etc/voxcast-server on POSIX, else the CWD (dev)."""
    explicit = os.environ.get("VOXCAST_SERVER_CONFIG")
    if explicit:
        return Path(explicit)
    if getattr(sys, "frozen", False):
        # sys.executable, not argv[0]: a bare-name PATH launch would resolve
        # argv[0] against the CWD and silently point at the wrong config.
        return Path(sys.executable).resolve().parent / "server.toml"
    if Path(sys.argv[0]).suffix.lower() == ".exe":
        return Path(sys.argv[0]).resolve().parent / "server.toml"
    if os.name == "posix":
        etc = Path("/etc/voxcast-server/server.toml")
        if etc.exists():
            return etc
    return Path.cwd() / "server.toml"


class ServerConfig(BaseSettings):
    model_config = SettingsConfigDict(
        env_prefix="VOXCAST_SERVER_",
        extra="ignore",
        toml_file=str(config_file_path()),
    )

    host: str = "127.0.0.1"
    port: int = 8590
    api_key: str = ""
    # Answer LAN discovery probes (UDP 8590) so the desktop client's
    # "Find server" button can list this server. Only active on a
    # non-loopback bind; the reply never contains the API key.
    discovery_enabled: bool = True
    # TLS: serve HTTPS with a self-signed cert (auto-generated in data_dir on
    # first HTTPS start unless tls_cert/tls_key point at your own PEMs). The
    # client trusts a self-signed cert via trust-on-first-use. For a public
    # FQDN, terminate TLS at a reverse proxy instead and leave this off.
    tls_enabled: bool = False
    tls_cert: str = ""   # PEM path; empty = auto self-signed in data_dir
    tls_key: str = ""    # PEM path; empty = auto self-signed in data_dir
    # Optional central AI enhancement: one company LLM key on the server, so
    # clients need none. Never exposed in /v1/status, logs or discovery.
    llm_provider: str = ""   # "anthropic" | "openai" | "" (off)
    llm_api_key: str = ""
    llm_model: str = ""      # empty = a sensible default per provider
    # Model tier served by this instance. "auto" picks the best tier for the
    # detected GPU at startup. The model is downloaded ON the server (getvoxcast
    # mirror, SHA-256 verified) into data_dir if it is not on disk yet.
    model: str = "auto"
    device: str = "auto"
    data_dir: str = ""
    max_upload_mb: int = 2048
    max_sync_audio_s: int = 600
    queue_size: int = 8
    job_ttl_s: int = 3600
    log_level: str = "INFO"

    @field_validator("model")
    @classmethod
    def validate_model(cls, v: str) -> str:
        v = (v or "auto").strip()
        return v if v in VALID_MODELS else "auto"

    @field_validator("device")
    @classmethod
    def validate_device(cls, v: str) -> str:
        v = (v or "auto").strip()
        return v if v in VALID_DEVICES else "auto"

    @field_validator("llm_provider")
    @classmethod
    def validate_llm_provider(cls, v: str) -> str:
        v = (v or "").strip().lower()
        return v if v in ("anthropic", "openai") else ""

    @field_validator("port")
    @classmethod
    def validate_port(cls, v: int) -> int:
        return v if 1 <= int(v) <= 65535 else 8590

    @classmethod
    def settings_customise_sources(
        cls,
        settings_cls,
        init_settings: PydanticBaseSettingsSource,
        env_settings: PydanticBaseSettingsSource,
        dotenv_settings: PydanticBaseSettingsSource,
        file_secret_settings: PydanticBaseSettingsSource,
    ):
        # Priority: init (tests) > env > server.toml > defaults
        return (
            init_settings,
            env_settings,
            TomlConfigSettingsSource(settings_cls),
        )


def load_config(**overrides) -> ServerConfig:
    return ServerConfig(**overrides)
