"""Frozen dataclasses for all Kuberoku domain objects.

All models are immutable after creation. SDK services return these;
the CLI formats them for display. No K8s-specific types leak through.
"""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime


@dataclass(frozen=True)
class ProcessFormation:
    """Desired state for a single process type (replicas + command)."""

    replicas: int
    command: str | None  # None = use image default (Dockerfile CMD/ENTRYPOINT)
    command_source: str  # "manual" | "procfile" | "default"


@dataclass(frozen=True)
class App:
    """An application managed by Kuberoku."""

    name: str
    cluster: str
    created_at: datetime
    created_by: str
    process_types: dict[str, ProcessFormation]
    release_version: int
    maintenance: bool


@dataclass(frozen=True)
class Dyno:
    """A running instance (pod) of a process type."""

    name: str  # "web.1", "worker.2" (presentation-only)
    app: str
    process_type: str  # "web", "worker"
    command: str | None  # command override (None = image default)
    state: str  # "up", "starting", "crashed", "down"
    started_at: datetime | None
    image: str
    node: str  # K8s node name


@dataclass(frozen=True)
class Release:
    """A point-in-time snapshot of a deployment."""

    version: int  # 1, 2, 3, ...
    app: str
    description: str  # "Deploy myapi:v2", "Set DATABASE_URL"
    images: dict[str, str]  # {"web": "myapi:v3", "worker": "myapi-worker:v3"}
    formation: dict[str, ProcessFormation]  # full formation snapshot
    commit: str | None  # git SHA (None if --image)
    ref: str | None  # git ref ("main", "v1.2.0", None if --image)
    created_at: datetime
    created_by: str
    env_diff: dict[str, str | None]  # {"KEY": "new_val"} or {"KEY": None} for unset


@dataclass(frozen=True)
class Addon:
    """An addon instance (postgres, redis, etc.) attached to an app."""

    instance_name: str  # "postgres", "analytics-db", "sessions-cache"
    addon_type: str  # "postgres", "redis"
    app: str
    status: str  # "running", "creating", "failed"
    image: str  # "postgres:16"
    cpu: str  # "250m"
    memory: str  # "256Mi"
    storage: str | None  # "1Gi" (None for ephemeral)
    env_key: str  # "DATABASE_URL"
    external: bool  # True = external URL, no StatefulSet
    ephemeral: bool  # True = no PVC
    created_at: datetime


@dataclass(frozen=True)
class Domain:
    """A custom domain attached to an app."""

    hostname: str  # "myapp.com"
    app: str
    process_type: str  # "web", "admin"
    port: int  # backend port
    tls: bool  # cert-manager active
    issuer: str | None  # "letsencrypt-prod" or None
    auto_generated: bool  # True = created by auto-domain
    status: str  # "active", "pending"
    created_at: datetime


@dataclass(frozen=True)
class IngressControllerInfo:
    """Detected Ingress controller details."""

    name: str  # "nginx", "traefik"
    class_name: str  # IngressClass name
    namespace: str  # Controller namespace
    supports_tcp_proxy: bool  # True if tcp-services CM supported


@dataclass(frozen=True)
class GatewayAllocation:
    """A port allocation in the gateway service."""

    external_port: int  # 10000-10999
    app: str
    target_service: str  # K8s Service name
    target_port: int  # backend port (6379, 5432, etc.)


@dataclass(frozen=True)
class LogLine:
    """A single log line from an app."""

    timestamp: datetime
    source: str  # "app" or "system"
    dyno: str  # "web.1"
    message: str


@dataclass(frozen=True)
class ClusterInfo:
    """Information about a configured K8s cluster."""

    name: str  # "production"
    context: str  # kubeconfig context
    is_current: bool
    k8s_version: str | None  # "1.35.0"
    node_count: int | None
    app_count: int | None


@dataclass(frozen=True)
class ExposedEndpoint:
    """Result of an expose/unexpose operation."""

    name: str  # "web", "postgres"
    app: str
    kind: str  # "process" or "addon"
    service_type: str  # "LoadBalancer", "NodePort", "ClusterIP"
    method: str  # "loadbalancer", "nodeport", "clusterip", "gateway"
    external_ip: str | None
    hostname: str | None
    ports: tuple[int, ...]
    gateway_port: int | None = None  # allocated gateway port (10000-10999)


@dataclass(frozen=True)
class PortMapping:
    """A port on a process type's Service."""

    port: int
    protocol: str  # "TCP" or "UDP"


@dataclass(frozen=True)
class ConnectionTarget:
    """Info returned by connect — used by CLI to run port-forward."""

    name: str  # "web", "postgres"
    app: str
    kind: str  # "process" or "addon"
    service_name: str  # K8s Service name to port-forward to
    namespace: str
    ports: tuple[PortMapping, ...]


@dataclass(frozen=True)
class ClusterConfig:
    """Cluster entry from ~/.kuberoku/config.yaml."""

    name: str
    context: str
    namespace: str | None = None
    base_domain: str | None = None
    resource_prefix: str | None = None
    label_domain: str | None = None
    registry: str | None = None


@dataclass(frozen=True)
class ExecResult:
    """Result of a non-interactive exec command."""

    output: str
    pod_name: str
    process_type: str


@dataclass(frozen=True)
class DetachedJob:
    """Result of a detached (background) exec command."""

    job_name: str
    app: str
    command: list[str]


@dataclass(frozen=True)
class AppStatus:
    """Aggregated status view for a single app."""

    app: App
    dynos: list[Dyno]
    config_count: int
    secret_count: int
    addons: list[Addon]
    domains: list[Domain]
    latest_release: Release | None
    ports: dict[str, list[PortMapping]]


@dataclass(frozen=True)
class MaintenanceInfo:
    """Maintenance state for an app."""

    enabled: bool  # Any process in maintenance
    app_wide: bool  # ALL processes via apps:maintenance:on
    since: datetime | None
    services: tuple[str, ...]  # Process types in maintenance
    saved_replicas: dict[str, int]  # Restore targets


@dataclass(frozen=True)
class RbacPermission:
    """A single RBAC permission check result."""

    resource: str
    api_group: str
    verb: str
    subresource: str
    required: bool  # True = required, False = elevated (optional)
    allowed: bool
    used_by: str  # feature that needs this


@dataclass(frozen=True)
class RbacReport:
    """Aggregated RBAC permission check result."""

    permissions: tuple[RbacPermission, ...]
    all_required_allowed: bool
    all_elevated_allowed: bool


@dataclass(frozen=True)
class RbacFixYaml:
    """Generated Role + RoleBinding YAML for missing permissions."""

    role_yaml: str
    rolebinding_yaml: str
    missing_count: int


@dataclass(frozen=True)
class PluginInfo:
    """Information about an installed or available plugin."""

    name: str  # command name ("mongodb")
    package: str  # PyPI package ("kuberoku-mongodb")
    version: str  # "1.0.0"
    description: str
    installed: bool


@dataclass(frozen=True)
class CheckResult:
    """Result of a single cluster requirement check."""

    name: str
    category: str
    passed: bool
    message: str
    detail: str | None = None
    fixable: bool = False
    fix_hint: str | None = None


@dataclass(frozen=True)
class FixResult:
    """Result of attempting to fix a failed check."""

    check_name: str
    fixed: bool
    message: str
    detail: str | None = None
