"""Mutable vCenter appliance networking / timesync state (lab persistence)."""

from __future__ import annotations

from typing import Any

from app.db.pool import Database
from app.vsphere.domain import api_state
from app.vsphere.errors import invalid_argument

_NETWORKING_KEY = "/api/appliance/networking"
_TIMESYNC_KEY = "/api/appliance/timesync"

_DEFAULT_NETWORKING: dict[str, Any] = {
    "hostname": "vcenter.lab.local",
    "node_name": "vcenter.lab.local",
    "default_gateway": "192.168.1.1",
    "dns": {
        "mode": "DHCP",
        "servers": ["8.8.8.8", "1.1.1.1"],
        "domains": ["lab.local"],
    },
    "interfaces": [
        {
            "name": "nic0",
            "status": "up",
            "mac": "00:50:56:aa:bb:cc",
            "ipv4": {"address": "192.168.1.50", "prefix": 24, "configurable": True},
        }
    ],
    "proxy": {"enabled": False, "server": "", "port": 0, "username": ""},
    "no_proxy": ["localhost", "127.0.0.1", ".lab.local"],
}

_DEFAULT_TIMESYNC: dict[str, Any] = {
    "mode": "NTP",
    "servers": ["time.lab.local"],
    "current_time": "2026-01-01T00:00:00.000Z",
}


def _merge_networking(payload: Any) -> dict[str, Any]:
    """Overlay stored fields onto the full lab default shape."""

    base = dict(_DEFAULT_NETWORKING)
    if not isinstance(payload, dict):
        return base
    merged = {**base, **payload}
    dns_base = dict(_DEFAULT_NETWORKING["dns"])
    dns_stored = payload.get("dns") if isinstance(payload.get("dns"), dict) else {}
    merged["dns"] = {**dns_base, **dns_stored}
    if not merged.get("interfaces"):
        merged["interfaces"] = list(_DEFAULT_NETWORKING["interfaces"])
    return merged


async def get_networking(database: Database) -> dict[str, Any]:
    payload = await api_state.get_payload_or_seed(database, "GET", _NETWORKING_KEY)
    if not isinstance(payload, dict):
        return {}
    dns = payload.get("dns") if isinstance(payload.get("dns"), dict) else {}
    if not list(dns.get("domains") or []) or not list(dns.get("servers") or []):
        restored = await api_state.restore_seed_payload(database, "GET", _NETWORKING_KEY)
        if isinstance(restored, dict):
            await save_networking(database, restored)
            return restored
    return payload


async def save_networking(database: Database, networking: dict[str, Any]) -> None:
    await api_state.put_payload(database, "GET", _NETWORKING_KEY, networking)
    # Keep Automation API GET mirrors in sync for stub/matrix probes.
    hostname = str(networking.get("hostname") or "vcenter.lab.local")
    dns = networking.get("dns") or {}
    await api_state.put_payload(
        database, "GET", "/api/appliance/networking/dns/hostname", {"name": hostname}
    )
    await api_state.put_payload(
        database,
        "GET",
        "/api/appliance/networking/dns/servers",
        {"mode": dns.get("mode") or "DHCP", "servers": list(dns.get("servers") or [])},
    )
    await api_state.put_payload(
        database,
        "GET",
        "/api/appliance/networking/dns/domains",
        list(dns.get("domains") or []),
    )


async def set_hostname(database: Database, name: str) -> str:
    hostname = name.strip()
    if not hostname:
        raise invalid_argument("name is required")
    networking = await get_networking(database)
    networking["hostname"] = hostname
    networking["node_name"] = hostname
    await save_networking(database, networking)
    return hostname


async def get_hostname(database: Database) -> str:
    networking = await get_networking(database)
    return str(networking.get("hostname") or "vcenter.lab.local")


async def set_dns_servers(
    database: Database,
    *,
    mode: str | None = None,
    servers: list[str] | None = None,
) -> dict[str, Any]:
    networking = await get_networking(database)
    dns = dict(networking.get("dns") or {})
    if mode is not None:
        dns["mode"] = mode
    if servers is not None:
        dns["servers"] = [str(s) for s in servers]
    networking["dns"] = dns
    await save_networking(database, networking)
    return {"mode": dns.get("mode") or "DHCP", "servers": list(dns.get("servers") or [])}


async def set_dns_domains(database: Database, domains: list[str]) -> list[str]:
    networking = await get_networking(database)
    dns = dict(networking.get("dns") or {})
    dns["domains"] = [str(d) for d in domains]
    networking["dns"] = dns
    await save_networking(database, networking)
    return list(dns["domains"])


async def get_timesync(database: Database) -> dict[str, Any]:
    payload = await api_state.get_payload_or_seed(database, "GET", _TIMESYNC_KEY)
    return payload if isinstance(payload, dict) else {}


async def seed_appliance_state(database: Database) -> None:
    """Write appliance networking/timesync seed baselines into vsphere_api_state."""

    networking = dict(_DEFAULT_NETWORKING)
    await api_state.put_seed_payload(database, "GET", _NETWORKING_KEY, networking)
    await api_state.put_seed_payload(database, "GET", _TIMESYNC_KEY, dict(_DEFAULT_TIMESYNC))
    await save_networking(database, networking)
    dns = networking.get("dns") or {}
    await api_state.put_seed_payload(
        database,
        "GET",
        "/api/appliance/networking/dns/hostname",
        {"name": networking["hostname"]},
    )
    await api_state.put_seed_payload(
        database,
        "GET",
        "/api/appliance/networking/dns/servers",
        {"mode": dns.get("mode") or "DHCP", "servers": list(dns.get("servers") or [])},
    )
    await api_state.put_seed_payload(
        database,
        "GET",
        "/api/appliance/networking/dns/domains",
        list(dns.get("domains") or []),
    )
