"""Configuration via environment variables."""

from __future__ import annotations

from pathlib import Path
from urllib.parse import urlparse

from pydantic import Field
from pydantic_settings import BaseSettings

DEFAULT_QUALITY_MODEL_MAP = {
    "fast": "turbo",
    "balanced": "large-v3-4bit",
    "best": "large-v3",
}

DEFAULT_BILLING_QUALITY_MULTIPLIERS = {
    "fast": 1.0,
    "balanced": 1.0,
    "best": 1.0,
}

APP_DOWNLOAD_ARTIFACTS = {
    ".dmg": {
        "filename": "Ambient.dmg",
        "media_type": "application/x-apple-diskimage",
    },
    ".zip": {
        "filename": "Ambient.app.zip",
        "media_type": "application/zip",
    },
}


class Settings(BaseSettings):
    database_path: str = "./hivecompute.db"
    client_api_key: str = ""
    host: str = "0.0.0.0"
    port: int = 8080

    # Reaper settings
    stale_job_minutes: int = 5
    reaper_interval_seconds: int = 60

    # Shard planner settings (must be positive)
    shard_length_seconds: float = 60.0
    shard_overlap_seconds: float = 1.5
    shard_max_length_seconds: float = 120.0

    # Validation settings
    canary_similarity_threshold: float = 0.7
    fallback_max_validation_failures: int = 2
    fallback_max_attempts: int = 3
    remote_audio_probe_enabled: bool = True
    remote_media_ingest_enabled: bool = True
    remote_audio_ingest_enabled: bool = False
    allow_local_audio_jobs: bool = False
    quality_model_map: dict[str, str] = Field(default_factory=lambda: DEFAULT_QUALITY_MODEL_MAP.copy())
    billing_quality_multipliers: dict[str, float] = Field(
        default_factory=lambda: DEFAULT_BILLING_QUALITY_MULTIPLIERS.copy()
    )

    def validated_shard_length(self) -> float:
        return max(15.0, self.shard_length_seconds)  # min 15s (MIN_SHARD_LENGTH)

    def validated_shard_max(self) -> float:
        return max(1.0, self.shard_max_length_seconds)

    def validated_canary_threshold(self) -> float:
        return max(0.0, min(1.0, self.canary_similarity_threshold))

    def validated_fallback_max_failures(self) -> int:
        return max(1, self.fallback_max_validation_failures)

    def validated_fallback_max_attempts(self) -> int:
        return max(1, self.fallback_max_attempts)

    def get_quality_model_map(self) -> dict[str, str]:
        mapping = DEFAULT_QUALITY_MODEL_MAP.copy()
        for quality, model in self.quality_model_map.items():
            normalized_quality = quality.strip().lower()
            normalized_model = model.strip()
            if normalized_quality in mapping and normalized_model:
                mapping[normalized_quality] = normalized_model
        return mapping

    def get_billing_quality_multipliers(self) -> dict[str, float]:
        multipliers = DEFAULT_BILLING_QUALITY_MULTIPLIERS.copy()
        for quality, multiplier in self.billing_quality_multipliers.items():
            normalized_quality = quality.strip().lower()
            if normalized_quality not in multipliers:
                continue
            try:
                normalized_multiplier = float(multiplier)
            except (TypeError, ValueError):
                continue
            if normalized_multiplier > 0:
                multipliers[normalized_quality] = normalized_multiplier
        return multipliers

    def get_billing_quality_multiplier(self, quality: str | None) -> float:
        if not quality:
            return 1.0

        normalized_quality = quality.strip().lower()
        return self.get_billing_quality_multipliers().get(normalized_quality, 1.0)

    def get_quality_tiers(self) -> list[str]:
        return list(DEFAULT_QUALITY_MODEL_MAP.keys())

    def get_quality_settings_summary(self) -> dict[str, dict[str, str | float]]:
        return {
            "quality_model_map": self.get_quality_model_map(),
            "billing_quality_multipliers": self.get_billing_quality_multipliers(),
        }

    def resolve_job_model(self, explicit_model: str | None, quality: str) -> tuple[str, str]:
        normalized_model = explicit_model.strip() if explicit_model else ""
        if normalized_model:
            return normalized_model, "explicit"

        normalized_quality = quality.strip().lower()
        return self.get_quality_model_map()[normalized_quality], "quality"

    # Legacy shared-secret fallback
    enrollment_secret: str = ""
    public_worker_enrollment_enabled: bool = False

    # Hosted site / manager settings
    site_name: str = "Ambient"
    manager_rp_name: str = "Ambient Manager"
    manager_origin: str = "http://localhost:8080"
    manager_rp_id: str = ""
    manager_session_hours: int = 12
    admin_bootstrap_secret: str = ""
    admin_bootstrap_users: str = "developer"
    app_download_url: str = ""
    app_download_file: str = ""

    model_config = {"env_prefix": "HIVE_"}

    def validate_secrets(self) -> None:
        """Crash early if secrets are not configured."""
        if not self.client_api_key:
            raise RuntimeError("HIVE_CLIENT_API_KEY must be set")

    def get_manager_origins(self) -> list[str]:
        origins = [origin.strip() for origin in self.manager_origin.split(",") if origin.strip()]
        return origins or ["http://localhost:8080"]

    def get_manager_rp_id(self) -> str:
        if self.manager_rp_id:
            return self.manager_rp_id

        hostname = urlparse(self.get_manager_origins()[0]).hostname
        return hostname or "localhost"

    def is_manager_cookie_secure(self) -> bool:
        return self.get_manager_origins()[0].startswith("https://")

    def get_bootstrap_users(self) -> list[str]:
        return [username.strip() for username in self.admin_bootstrap_users.split(",") if username.strip()]

    def should_require_app_download_url(self) -> bool:
        for origin in self.get_manager_origins():
            hostname = urlparse(origin).hostname
            if hostname and hostname not in {"localhost", "127.0.0.1", "testserver"}:
                return True
        return False

    def get_app_download_url(self, *, validate: bool = False) -> str | None:
        url = self.app_download_url.strip()
        if not url:
            return None

        if validate:
            parsed = urlparse(url)
            if parsed.scheme not in {"http", "https"} or not parsed.netloc:
                raise ValueError("HIVE_APP_DOWNLOAD_URL must be a full http(s) URL.")
            if parsed.scheme != "https" and parsed.hostname not in {"localhost", "127.0.0.1"}:
                raise ValueError("HIVE_APP_DOWNLOAD_URL must use HTTPS outside localhost.")
            if self._app_download_extension(parsed.path) is None:
                raise ValueError("HIVE_APP_DOWNLOAD_URL must point to a .dmg or .zip artifact.")

        return url

    def get_app_download_file(self, *, validate: bool = False) -> Path | None:
        raw = self.app_download_file.strip()
        if not raw:
            return None

        path = Path(raw).expanduser()
        if validate:
            if self._app_download_extension(path.name) is None:
                raise ValueError("HIVE_APP_DOWNLOAD_FILE must point to a .dmg or .zip artifact.")
            if not path.exists():
                raise ValueError(f"HIVE_APP_DOWNLOAD_FILE does not exist: {path}")
            if not path.is_file():
                raise ValueError(f"HIVE_APP_DOWNLOAD_FILE is not a file: {path}")

        return path

    def get_app_download_source(self, *, validate: bool = False) -> tuple[str, str | Path] | None:
        app_download_url = self.get_app_download_url(validate=validate)
        app_download_file = self.get_app_download_file(validate=validate)

        if app_download_url and app_download_file:
            raise ValueError("Set only one of HIVE_APP_DOWNLOAD_URL or HIVE_APP_DOWNLOAD_FILE.")
        if app_download_file:
            return ("file", app_download_file)
        if app_download_url:
            return ("url", app_download_url)
        return None

    def get_app_download_filename(self, *, validate: bool = False) -> str:
        extension = self.get_app_download_extension(validate=validate)
        return APP_DOWNLOAD_ARTIFACTS[extension]["filename"]

    def get_app_download_media_type(self, *, validate: bool = False) -> str:
        extension = self.get_app_download_extension(validate=validate)
        return APP_DOWNLOAD_ARTIFACTS[extension]["media_type"]

    def get_public_app_download_route(self, *, validate: bool = False) -> str:
        return f"/download/{self.get_app_download_filename(validate=validate)}"

    def get_app_download_extension(self, *, validate: bool = False) -> str:
        source = self.get_app_download_source(validate=validate)
        if not source:
            return ".dmg"

        _, source_value = source
        raw_path = source_value if isinstance(source_value, str) else source_value.name
        extension = self._app_download_extension(str(raw_path))
        if extension is None:
            raise ValueError("App download source must point to a .dmg or .zip artifact.")
        return extension

    @staticmethod
    def _app_download_extension(path_or_url: str) -> str | None:
        lowered = path_or_url.lower()
        if lowered.endswith(".app.zip") or lowered.endswith(".zip"):
            return ".zip"
        if lowered.endswith(".dmg"):
            return ".dmg"
        return None


def get_settings() -> Settings:
    return Settings()
