"""Configuration models for Omni SDK."""

from __future__ import annotations

from pathlib import Path
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field, field_validator


class RetryConfig(BaseModel):
    model_config = ConfigDict(extra="forbid")

    max_attempts: int = 4
    base_delay: float = 0.2
    max_delay: float = 2.5
    jitter: float = 0.2
    retry_statuses: list[int] = Field(default_factory=lambda: [429, 500, 502, 503, 504])


class CacheConfig(BaseModel):
    model_config = ConfigDict(extra="forbid")

    enabled: bool = True
    ttl_defaults: dict[str, float] = Field(
        default_factory=lambda: {
            "omni.resources.ResourceService.Get": 5.0,
            "omni.resources.ResourceService.List": 5.0,
            "omni.resources.ResourceService.Controllers": 5.0,
            "omni.resources.ResourceService.DependencyGraph": 5.0,
            "management.ManagementService.Kubeconfig": 12.0,
            "management.ManagementService.Talosconfig": 12.0,
            "management.ManagementService.Omniconfig": 12.0,
            "management.ManagementService.ListServiceAccounts": 15.0,
        }
    )
    max_entries: int = 1000
    backend: Literal["sqlite", "memory"] = "sqlite"


class AuthConfig(BaseModel):
    model_config = ConfigDict(extra="forbid")

    service_account_key: str | None = None
    service_account_env_aliases: list[str] = Field(
        default_factory=lambda: ["OMNI_SERVICE_ACCOUNT_KEY", "SIDERO_SERVICE_ACCOUNT_KEY"]
    )
    identity: str | None = None
    key_fingerprint: str | None = None


class OmniInstanceConfig(BaseModel):
    model_config = ConfigDict(extra="forbid")

    url: str
    verify_tls: bool | str = True
    auth: AuthConfig = Field(default_factory=AuthConfig)
    timeout: float = 30.0
    retry: RetryConfig = Field(default_factory=RetryConfig)
    cache: CacheConfig = Field(default_factory=CacheConfig)


class Preferences(BaseModel):
    model_config = ConfigDict(extra="allow")

    default_cluster: str | None = None
    default_instance: str | None = None
    instance_clusters: dict[str, str] = Field(default_factory=dict)


class OmniSDKConfig(BaseModel):
    model_config = ConfigDict(extra="forbid")

    version: str = "1"
    default_instance: str | None = None
    instances: dict[str, OmniInstanceConfig] = Field(default_factory=dict)
    preferences: Preferences = Field(default_factory=Preferences)

    @field_validator("instances")
    @classmethod
    def validate_instances(cls, value: dict[str, OmniInstanceConfig]) -> dict[str, OmniInstanceConfig]:
        for key in value:
            if not key.strip():
                raise ValueError("instance names must not be empty")
        return value

    def pick_instance(self, explicit: str | None = None) -> tuple[str, OmniInstanceConfig]:
        if explicit:
            if explicit not in self.instances:
                raise KeyError(f"instance '{explicit}' not found in config")
            return explicit, self.instances[explicit]

        selected = self.default_instance or self.preferences.default_instance
        if selected and selected in self.instances:
            return selected, self.instances[selected]

        if self.instances:
            name = next(iter(self.instances))
            return name, self.instances[name]

        raise ValueError("no Omni instances configured")


class ResolvedConfig(BaseModel):
    model_config = ConfigDict(extra="forbid")

    source: str
    path: Path | None = None
    data: OmniSDKConfig


ConfigInput = OmniSDKConfig | dict[str, Any]
