"""Data models for Setup Core V2.

All structures are immutable (frozen dataclasses) and carry no I/O concerns.
The engine uses these as pure value types.
"""

from dataclasses import dataclass, field
from typing import Literal

# ---------------------------------------------------------------------------
# Type aliases
# ---------------------------------------------------------------------------

AuthMode = Literal["api_key", "oauth", "auto"]
GapKind = Literal["telegram_token", "provider_api_key", "oauth_credentials"]

# ---------------------------------------------------------------------------
# Provider metadata (static, no I/O)
# ---------------------------------------------------------------------------

#: Map from canonical provider name -> required environment variable.
#: ``None`` means the provider needs no API key (e.g. local/Ollama).
PROVIDER_ENV_VARS: dict[str, str | None] = {
    "anthropic": "ANTHROPIC_API_KEY",
    "openai": "OPENAI_API_KEY",
    "google": "GOOGLE_API_KEY",
    "copilot": "COPILOT_API_KEY",
    "openrouter": "OPENROUTER_API_KEY",
    "cerebras": "CEREBRAS_API_KEY",
    "ollama": None,
}

#: Providers that support OAuth authentication.
OAUTH_CAPABLE_PROVIDERS: frozenset[str] = frozenset({"anthropic", "openai", "google", "copilot"})

#: Model-ID prefixes that identify an OAuth-capable provider.
OAUTH_MODEL_PREFIXES: dict[str, tuple[str, ...]] = {
    "anthropic": ("anthropic", "claude-code"),
    "openai": ("openai", "codex"),
    "google": ("google", "gemini-cli"),
    "copilot": ("copilot", "github_copilot"),
}


@dataclass(frozen=True)
class SetupChoice:
    """User's declared setup inputs."""

    model: str
    channels: tuple[str, ...]
    auth_mode: str = "api_key"
    provider_hint: str | None = None
    oauth_credentials_present: bool = False
    telegram_token: str | None = None
    provider_api_key: str | None = None

    # Identity / bot metadata (used by the apply layer to write config.toml).
    owner_name: str = ""
    owner_telegram_id: int | None = None
    workspace_root: str = "~/.otto/workspace"
    workspace_mode: str = "default"
    workspace_sandbox: str = "none"
    workspace_sandbox_network: bool = True
    bot_name: str = "default"


@dataclass(frozen=True)
class SetupRequirements:
    """Computed requirements given a SetupChoice."""

    channels: tuple[str, ...]
    auth_mode: str
    model_provider: str | None
    oauth_provider: str | None
    provider_env_var: str | None
    requires_telegram_token: bool
    requires_provider_api_key: bool
    requires_oauth_credentials: bool


@dataclass(frozen=True)
class CredentialGap:
    """A missing or invalid credential that blocks setup completion."""

    kind: GapKind
    env_var: str | None = None
    provider: str | None = None
    message: str = ""


@dataclass(frozen=True)
class SetupPlan:
    """Validated, ready-to-inspect setup plan."""

    requirements: SetupRequirements
    gaps: tuple[CredentialGap, ...] = field(default_factory=tuple)
    secrets: dict[str, str] = field(default_factory=dict)
    # The original user choices — used by apply_plan to build config.toml.
    choice: SetupChoice | None = None

    @property
    def is_complete(self) -> bool:
        return len(self.gaps) == 0


class SetupValidationError(ValueError):
    """Raised for invalid setup choices (not merely missing credentials)."""
