"""Pydantic models for remote host management."""

from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import Annotated

from pydantic import BaseModel, ConfigDict, Field, field_validator


class HostConnectionStatus(str, Enum):
    """Connection status for a remote host."""

    UNKNOWN = "unknown"
    REACHABLE = "reachable"
    UNREACHABLE = "unreachable"
    AUTH_FAILED = "auth_failed"
    TIMEOUT = "timeout"
    ERROR = "error"


class HostConfig(BaseModel):
    """SSH connection configuration for a remote host."""

    model_config = ConfigDict(frozen=True)

    name: Annotated[
        str,
        Field(min_length=1, max_length=64, pattern=r"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"),
    ] = Field(..., description="Friendly identifier for the host")

    hostname: str = Field(..., description="SSH hostname or IP address")
    user: str = Field(default="root", description="SSH username")
    port: Annotated[int, Field(ge=1, le=65535)] = Field(
        default=22, description="SSH port"
    )

    key_path: str | None = Field(
        default=None, description="Path to SSH private key file"
    )
    key_secret_name: str | None = Field(
        default=None, description="Name of secret containing SSH key"
    )

    connect_timeout: Annotated[int, Field(ge=1, le=300)] = Field(
        default=10, description="SSH connection timeout in seconds"
    )
    strict_host_key_checking: bool = Field(
        default=False, description="Enable strict SSH host key checking"
    )

    gaol_path: str = Field(
        default="gaol", description="Path to gaol binary on remote host"
    )
    use_sudo: bool = Field(
        default=True, description="Use sudo when running gaol commands (needed for nspawn/libvirt)"
    )

    description: str | None = None
    tags: list[str] = Field(default_factory=list)

    @field_validator("hostname")
    @classmethod
    def validate_hostname(cls, v: str) -> str:
        """Validate hostname is not empty."""
        if not v.strip():
            raise ValueError("Hostname cannot be empty")
        return v.strip()

    @property
    def ssh_destination(self) -> str:
        """Return user@hostname format."""
        return f"{self.user}@{self.hostname}"


class HostState(BaseModel):
    """Current state of a remote host connection."""

    model_config = ConfigDict(frozen=False)

    status: HostConnectionStatus = HostConnectionStatus.UNKNOWN
    last_check: datetime | None = None
    last_successful_connection: datetime | None = None
    error_message: str | None = None
    gaol_version: str | None = None
    os_info: str | None = None


class StoredHost(BaseModel):
    """Stored host configuration with metadata."""

    model_config = ConfigDict(frozen=False)

    config: HostConfig
    state: HostState = Field(default_factory=HostState)
    created_at: datetime = Field(default_factory=datetime.now)
    updated_at: datetime = Field(default_factory=datetime.now)

    def update_state(
        self,
        status: HostConnectionStatus,
        error_message: str | None = None,
        gaol_version: str | None = None,
    ) -> None:
        """Update the host state."""
        self.state.status = status
        self.state.last_check = datetime.now()
        self.state.error_message = error_message
        self.updated_at = datetime.now()

        if status == HostConnectionStatus.REACHABLE:
            self.state.last_successful_connection = datetime.now()
            self.state.error_message = None
            if gaol_version:
                self.state.gaol_version = gaol_version


class RemoteCommandResult(BaseModel):
    """Result from executing a command on a remote host."""

    model_config = ConfigDict(frozen=True)

    host_name: str
    exit_code: int
    stdout: str
    stderr: str
    duration_ms: int
    parsed_json: dict | list | None = None

    @property
    def success(self) -> bool:
        """Return True if command succeeded."""
        return self.exit_code == 0


class HostsRegistry(BaseModel):
    """Central registry of all hosts."""

    model_config = ConfigDict(frozen=False)

    version: int = 1
    hosts: dict[str, StoredHost] = Field(default_factory=dict)

    def add_host(self, host: StoredHost) -> None:
        """Add or update a host."""
        self.hosts[host.config.name] = host

    def get_host(self, name: str) -> StoredHost | None:
        """Get a host by name."""
        return self.hosts.get(name)

    def remove_host(self, name: str) -> bool:
        """Remove a host. Returns True if removed."""
        if name in self.hosts:
            del self.hosts[name]
            return True
        return False

    def list_hosts(self) -> list[StoredHost]:
        """List all hosts."""
        return list(self.hosts.values())
