"""All shared Pydantic v2 models for the vmcluster-mcp data contract."""

from __future__ import annotations

from typing import Any, Generic, Literal, TypeVar

from pydantic import BaseModel

T = TypeVar("T")


# --- Error Contract (design doc section 2.8) ---


class ErrorDetail(BaseModel):
    code: str
    message: str
    node: str | None = None
    recoverable: bool = False


class ToolResult(BaseModel, Generic[T]):
    success: bool
    result: T | None = None
    error: ErrorDetail | None = None


# --- Cluster Types (design doc sections 2.1, 2.4) ---


class NodeDescriptor(BaseModel):
    name: str
    role: str
    ip: str
    ssh_port: int = 22
    ssh_user: str = "root"
    ssh_key_path: str


class ClusterHandle(BaseModel):
    cluster_name: str
    nodes: list[NodeDescriptor]
    artifact_path: str
    kernel_version: str


class NodeStatus(BaseModel):
    name: str
    role: str
    ip: str
    domain_state: Literal["running", "shut off", "paused", "crashed", "unknown"]
    ssh_reachable: bool
    artifact_id_installed: str | None = None
    loaded_modules: list[str] = []


class ClusterStatus(BaseModel):
    cluster_name: str
    network_active: bool
    nodes: list[NodeStatus]


# --- Execution Types (design doc section 2.4) ---


class ExecResult(BaseModel):
    exit_code: int
    stdout: str
    stderr: str
    duration_s: float
    timed_out: bool


class ExecAllResult(BaseModel):
    results: dict[str, ExecResult]
    all_succeeded: bool
    failed_nodes: list[str]


# --- Snapshot Types ---


class SnapshotNodeInfo(BaseModel):
    node_name: str
    disk_path: str
    size_bytes: int


class SnapshotInfo(BaseModel):
    snapshot_name: str
    cluster_name: str
    created_at: str
    per_node: dict[str, SnapshotNodeInfo]


class SnapshotDeleteNodeResult(BaseModel):
    node_name: str
    deleted: bool
    error: str | None = None


class SnapshotDeleteResult(BaseModel):
    snapshot_name: str
    cluster_name: str
    per_node: dict[str, SnapshotDeleteNodeResult]


# --- Artifact Types (design doc sections 2.3, 2.4) ---


class BuildArtifact(BaseModel):
    artifact_id: str
    source_path: str
    build_type: Literal["raw_tree", "make_install", "rpm", "deb"]
    kernel_version: str
    modules: list[str]
    userspace_bins: list[str]
    test_framework_path: str | None = None
    git_sha: str | None = None
    git_branch: str | None = None
    build_timestamp: str
    metadata: dict[str, str] = {}


class SyncNodeResult(BaseModel):
    node_name: str
    bytes_transferred: int
    duration_s: float
    rsync_exit_code: int
    skipped: bool


class SyncStatus(BaseModel):
    per_node: dict[str, SyncNodeResult]


class InstallNodeResult(BaseModel):
    node_name: str
    exit_code: int
    installed_module_paths: list[str]
    depmod_output: str


class InstallStatus(BaseModel):
    per_node: dict[str, InstallNodeResult]


class ArtifactDiff(BaseModel):
    artifact_id_a: str
    artifact_id_b: str
    changed_modules: list[str]  # .ko filenames that differ by hash
    changed_bins: list[str]     # userspace binary filenames that differ
    git_log: str | None = None  # git log a_sha..b_sha if both have git_sha


class CompilerError(BaseModel):
    file: str
    line: int
    column: int | None = None
    message: str


class CompilerWarning(BaseModel):
    file: str
    line: int
    column: int | None = None
    message: str


class ArtifactBuildResult(BaseModel):
    success: bool
    artifact: BuildArtifact | None = None
    errors: list[CompilerError] = []
    warnings: list[CompilerWarning] = []
    duration_s: float
    stdout_tail: str


# --- Observability Types (design doc section 2.4) ---


class DmesgLine(BaseModel):
    timestamp: float
    level: Literal["debug", "info", "notice", "warning", "err", "crit", "alert", "emerg"]
    facility: str
    text: str
    classified_as: Literal["oops", "panic", "warn", "kasan", "ubsan", "normal"] | None = None


class DmesgResult(BaseModel):
    lines: list[DmesgLine]
    oops_detected: bool
    panic_detected: bool
    warning_count: int
    error_count: int


# --- Network Fault Types (design doc section 2.4) ---


class NetFaultHandle(BaseModel):
    handle_id: str
    fault_type: Literal["partition", "impairment"]
    affected_nodes: list[str]
    created_at: str


class NetFaultInfo(BaseModel):
    handle_id: str
    fault_type: Literal["partition", "impairment"]
    affected_nodes: list[str]
    created_at: str
    parameters: dict[str, Any]
