"""Host selection and session value objects."""
from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import Literal, Mapping


@dataclass(frozen=True)
class CurrentSessionModelAttestation:
    """Host-observed identity of the already running lead session."""

    provider_id: str
    observed_model: str | None
    normalized_model_ref: str | None
    level: Literal["exact", "channel", "unknown"]
    source: Literal[
        "host-session-metadata",
        "native-api",
        "cli-handoff",
        "unavailable",
    ]
    # 호스트가 알려 줄 때만 채운다. 스크레이핑하지 않는다.
    effort: str | None = None

    @classmethod
    def unknown(
        cls,
        provider_id: str,
        source: Literal["unavailable"] = "unavailable",
    ) -> CurrentSessionModelAttestation:
        return cls(provider_id, None, None, "unknown", source, None)


def _unavailable_current_model() -> CurrentSessionModelAttestation:
    return CurrentSessionModelAttestation.unknown("")


@dataclass(frozen=True)
class HostDescriptor:
    id: str
    aliases: tuple[str, ...]
    native_provider_id: str
    required_executables: tuple[str, ...]
    launch_mode: Literal["lead", "team"]
    install_targets: frozenset[str]
    agent_id: str
    agent_label: str
    role: str
    dispatch_mode: str
    session_accounting: str
    has_claude_session: bool
    relay_contract: str


@dataclass(frozen=True)
class HostSessionContext:
    host_id: str
    entry_mode: Literal["spawn-process", "current-session"]
    available_functions: frozenset[str]
    interaction_surface: str
    current_model: CurrentSessionModelAttestation = field(
        default_factory=_unavailable_current_model
    )


@dataclass(frozen=True)
class HostResolutionContext:
    explicit_env_host: str
    signals: frozenset[str]


@dataclass(frozen=True)
class HostClaim:
    host_id: str
    priority: int
    reason: str


@dataclass(frozen=True)
class HostReadiness:
    ok: bool
    effective_functions: frozenset[str]
    missing_executables: tuple[str, ...]
    checks: tuple[Mapping[str, object], ...]
    reason: str


@dataclass(frozen=True)
class LeadStartRequest:
    project_root: Path
    task_type: str
    argv: tuple[str, ...]


@dataclass(frozen=True)
class LeadResumeRequest:
    project_root: Path
    task_type: str
    session_id: str
    argv: tuple[str, ...]


@dataclass(frozen=True)
class LeadSessionPlan:
    mode: Literal["spawn-process", "current-session"]
    argv: tuple[str, ...]
    env: Mapping[str, str]
    sandbox_note: str


class HostNotRegistered(ValueError):
    """Raised when no registered host matches an explicit request."""


class HostUnavailable(RuntimeError):
    """Raised when no single host can serve an automatic request."""


class HostCapabilityMismatch(ValueError):
    """Raised when a selected host lacks a required semantic capability."""


class HostAdapterContractError(ValueError):
    """Raised when a host manifest or factory violates the adapter contract."""


class ProviderNotRegistered(ValueError):
    """Raised when a host references an unknown native provider."""


class ProviderUnavailable(RuntimeError):
    """Raised when a registered provider cannot serve a request."""
