"""Deterministic vSphere inventory seed for labs."""

from __future__ import annotations

import os
from typing import Any

from app.db.pool import Database
from app.security.auth import hash_secret
from app.vsphere import inventory
from app.vsphere.domain.api_state import seed_api_surface
from app.vsphere.domain.appliance import seed_appliance_state
from app.vsphere.domain.content import seed_platform_extras
from app.vsphere.domain.platform_surface import seed_platform_surface
from app.vsphere.profiles import VsphereSeedProfile, build_vsphere_profile, infer_profile_hint, props_json
from app.vsphere.security.session import ensure_default_credentials


async def _seed_platform(database: Database, *, extras_scale: int = 1) -> dict[str, Any]:
    """Libraries/tags/files + full Automation API surface state (all profiles)."""

    extras: dict[str, Any] = {}
    try:
        await seed_platform_extras(database, scale=extras_scale)
        extras["platform_extras"] = True
        extras["extras_scale"] = extras_scale
    except Exception as error:
        extras["platform_extras_error"] = str(error)
    try:
        extras["api_surface"] = await seed_api_surface(database)
    except Exception as error:
        extras["api_surface_error"] = str(error)
    try:
        await seed_appliance_state(database)
        extras["appliance"] = True
    except Exception as error:
        extras["appliance_error"] = str(error)
    try:
        extras["platform_surface"] = await seed_platform_surface(database)
    except Exception as error:
        extras["platform_surface_error"] = str(error)
    return extras


async def seed_vsphere_inventory(
    database: Database,
    *,
    force: bool = False,
    profile: str | None = None,
    large_hosts: int | None = None,
    large_vms: int | None = None,
) -> dict[str, Any]:
    """Populate vCenter-like inventory from a named profile (default: large / 1000 VMs)."""

    resolved = build_vsphere_profile(profile, large_hosts=large_hosts, large_vms=large_vms)
    existing = await inventory.count_objects(database)
    if existing and not force:
        await ensure_default_credentials(database)
        platform = await _seed_platform(database, extras_scale=resolved.extras_scale)
        by_type = await inventory.count_by_type(database)
        return {
            "seeded": False,
            "profile": resolved.name,
            "objects": existing,
            "by_type": by_type,
            "vms": by_type.get("VirtualMachine", 0),
            "hosts": by_type.get("HostSystem", 0),
            **platform,
        }

    await _wipe(database)
    await _apply_profile(database, resolved)
    await ensure_default_credentials(database)
    platform = await _seed_platform(database, extras_scale=resolved.extras_scale)
    by_type = await inventory.count_by_type(database)
    return {
        "seeded": True,
        "profile": resolved.name,
        "objects": await inventory.count_objects(database),
        "by_type": by_type,
        "vms": by_type.get("VirtualMachine", 0),
        "hosts": by_type.get("HostSystem", 0),
        **platform,
    }


async def _wipe(database: Database) -> None:
    from app.vsphere.domain.content import clear_transfer_sessions
    from app.vsphere.soap.property_collector import clear_pc_state

    await clear_pc_state(database)
    await clear_transfer_sessions(database)
    pool = database.pool  # type: ignore[attr-defined]
    async with pool.acquire() as conn:
        await conn.execute(
            """
            DO $$ BEGIN
              IF to_regclass('public.vsphere_nfc_leases') IS NOT NULL THEN
                DELETE FROM vsphere_nfc_leases;
              END IF;
              IF to_regclass('public.vsphere_transfer_sessions') IS NOT NULL THEN
                DELETE FROM vsphere_transfer_sessions;
              END IF;
              IF to_regclass('public.vsphere_console_tickets') IS NOT NULL THEN
                DELETE FROM vsphere_console_tickets;
              END IF;
              IF to_regclass('public.vsphere_pc_state') IS NOT NULL THEN
                DELETE FROM vsphere_pc_state;
              END IF;
            END $$;
            """
        )
        await conn.execute("DELETE FROM vsphere_permissions")
        await conn.execute("DELETE FROM vsphere_datastore_files")
        await conn.execute("DELETE FROM vsphere_snapshots")
        await conn.execute("DELETE FROM vsphere_tag_associations")
        await conn.execute("DELETE FROM vsphere_tags")
        await conn.execute("DELETE FROM vsphere_tag_categories")
        await conn.execute("DELETE FROM vsphere_library_items")
        await conn.execute("DELETE FROM vsphere_libraries")
        await conn.execute("DELETE FROM vsphere_tasks")
        await conn.execute(
            """
            DO $$ BEGIN
              IF to_regclass('public.vsphere_api_state') IS NOT NULL THEN
                DELETE FROM vsphere_api_state;
              END IF;
            END $$;
            """
        )
        await conn.execute("UPDATE vsphere_objects SET parent_moid = NULL")
        await conn.execute("DELETE FROM vsphere_objects")


async def _apply_profile(database: Database, profile: VsphereSeedProfile) -> None:
    rows = [
        {
            "moid": obj.moid,
            "type": obj.type,
            "name": obj.name,
            "parent_moid": obj.parent_moid,
            "props": obj.props,
        }
        for obj in profile.objects
    ]
    # Parents first: folders → dc → cluster → hosts/vms. Spec order already topological.
    await inventory.upsert_objects_batch(database, rows)

    pool = database.pool  # type: ignore[attr-defined]
    async with pool.acquire() as conn:
        for cred in profile.credentials:
            await conn.execute(
                """
                INSERT INTO vsphere_credentials (username, password_hash, roles)
                VALUES ($1, $2, $3)
                ON CONFLICT (username) DO UPDATE SET
                  password_hash = EXCLUDED.password_hash,
                  roles = EXCLUDED.roles
                """,
                cred.username,
                hash_secret(cred.password),
                list(cred.roles),
            )
        for perm in profile.permissions:
            await conn.execute(
                """
                INSERT INTO vsphere_permissions (principal, role, entity_moid, propagate)
                VALUES ($1, $2, $3, $4)
                """,
                perm.principal,
                perm.role,
                perm.entity_moid,
                perm.propagate,
            )


def default_profile_name() -> str:
    return os.getenv("SEED_VSPHERE_PROFILE", "large")


async def vsphere_state_summary(database: Database) -> dict[str, Any]:
    by_type = await inventory.count_by_type(database)
    hosts = by_type.get("HostSystem", 0)
    vms = by_type.get("VirtualMachine", 0)
    datastores = by_type.get("Datastore", 0)
    networks = by_type.get("Network", 0) + by_type.get("DistributedVirtualPortgroup", 0)
    return {
        "hosts": hosts,
        "vms": vms,
        "datastores": datastores,
        "networks": networks,
        "datacenters": by_type.get("Datacenter", 0),
        "clusters": by_type.get("ClusterComputeResource", 0),
        "objects": sum(by_type.values()),
        "by_type": by_type,
        "profile_hint": infer_profile_hint(hosts=hosts, vms=vms, datastores=datastores),
    }


__all__ = [
    "default_profile_name",
    "props_json",
    "seed_vsphere_inventory",
    "vsphere_state_summary",
]
