"""Permissions, identity, guest extras, host detail, console tickets."""

from __future__ import annotations

from typing import Any

from fastapi import APIRouter, Depends, Query, Response

from app.db.pool import Database
from app.dependencies import get_database
from app.vsphere import inventory
from app.vsphere.domain import vm_ops
from app.vsphere.errors import invalid_argument, not_found
from app.vsphere.security.authz import require_admin, require_privilege, require_read
from app.vsphere.security.session import SessionInfo

router = APIRouter(tags=["vSphere Platform"])


@router.get("/api/vcenter/privilege")
async def list_privileges(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, str]]:
    from app.vsphere.domain import api_state

    payload = await api_state.get_payload(database, "GET", "/api/vcenter/privilege")
    return payload if isinstance(payload, list) else []


@router.get("/api/vcenter/authorization/roles")
async def list_roles(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    from app.vsphere.domain import api_state

    payload = await api_state.get_payload(database, "GET", "/api/vcenter/authorization/roles")
    return payload if isinstance(payload, list) else []


@router.get("/api/vcenter/authorization/permissions")
async def list_permissions(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    pool = database.pool  # type: ignore[attr-defined]
    async with pool.acquire() as conn:
        rows = await conn.fetch(
            "SELECT id, principal, role, entity_moid, propagate FROM vsphere_permissions ORDER BY id"
        )
    return [
        {
            "id": row["id"],
            "principal": row["principal"],
            "role": row["role"],
            "entity": row["entity_moid"],
            "propagate": row["propagate"],
        }
        for row in rows
    ]


@router.post("/api/vcenter/authorization/permissions")
async def create_permission(
    body: dict[str, Any],
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_admin),
) -> dict[str, Any]:
    principal = str(body.get("principal") or "").strip()
    role = str(body.get("role") or "").strip()
    if not principal or not role:
        raise invalid_argument("principal and role are required")
    entity = body.get("entity") or body.get("entity_moid")
    propagate = bool(body.get("propagate", True))
    pool = database.pool  # type: ignore[attr-defined]
    async with pool.acquire() as conn:
        row = await conn.fetchrow(
            """
            INSERT INTO vsphere_permissions (principal, role, entity_moid, propagate)
            VALUES ($1, $2, $3, $4)
            RETURNING id
            """,
            principal,
            role,
            None if entity in (None, "", "null") else str(entity),
            propagate,
        )
    return {"id": row["id"]}


@router.delete("/api/vcenter/authorization/permissions/{permission_id}")
async def delete_permission(
    permission_id: int,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_admin),
) -> Response:
    pool = database.pool  # type: ignore[attr-defined]
    async with pool.acquire() as conn:
        result = await conn.execute(
            "DELETE FROM vsphere_permissions WHERE id = $1",
            permission_id,
        )
    if not result.endswith("1"):
        raise not_found(f"Permission {permission_id} not found")
    return Response(status_code=204)


@router.get("/api/vcenter/namespaces")
async def list_namespaces(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    from app.vsphere.domain import api_state

    payload = await api_state.get_payload(database, "GET", "/api/vcenter/namespaces")
    return payload if isinstance(payload, list) else []


@router.get("/api/vcenter/namespace-management/nsx-tier0-gateway")
async def nsx_tier0_gateway(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    from app.vsphere.domain import api_state

    path = "/api/vcenter/namespace-management/nsx-tier0-gateway"
    payload = await api_state.get_payload(database, "GET", path)
    return payload if isinstance(payload, list) else []


@router.get("/api/vcenter/namespace-management/networks")
async def wcp_networks(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    from app.vsphere.domain import api_state

    path = "/api/vcenter/namespace-management/networks"
    payload = await api_state.get_payload(database, "GET", path)
    return payload if isinstance(payload, list) else []


@router.get("/api/vcenter/namespace-management/virtual-machine-classes")
async def vm_classes(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    from app.vsphere.domain import api_state

    path = "/api/vcenter/namespace-management/virtual-machine-classes"
    payload = await api_state.get_payload(database, "GET", path)
    return payload if isinstance(payload, list) else []


@router.get("/api/vcenter/identity/providers")
async def identity_providers(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    from app.vsphere.domain import platform_surface

    return await platform_surface.list_identity_providers(database)


@router.post("/api/vcenter/identity/providers")
async def create_identity_provider(
    body: dict[str, Any],
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_admin),
) -> dict[str, Any]:
    from app.vsphere.domain import platform_surface

    return await platform_surface.upsert_identity_provider(database, body)


@router.get("/api/vcenter/identity/providers/{provider}")
async def get_identity_provider(
    provider: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, Any]:
    from app.vsphere.domain import platform_surface

    item = await platform_surface.get_identity_provider(database, provider)
    if item is not None:
        return item
    raise not_found(f"Identity provider {provider} not found")


@router.patch("/api/vcenter/identity/providers/{provider}")
@router.put("/api/vcenter/identity/providers/{provider}")
async def update_identity_provider(
    provider: str,
    body: dict[str, Any],
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_admin),
) -> dict[str, Any]:
    from app.vsphere.domain import platform_surface

    payload = {**body, "provider": provider}
    return await platform_surface.upsert_identity_provider(database, payload)


@router.delete("/api/vcenter/identity/providers/{provider}")
async def delete_identity_provider(
    provider: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_admin),
) -> Response:
    from app.vsphere.domain import platform_surface

    if not await platform_surface.delete_identity_provider(database, provider):
        raise not_found(f"Identity provider {provider} not found")
    return Response(status_code=204)


@router.get("/api/vcenter/certificate-management/vcenter/tls")
async def machine_tls(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, Any]:
    from app.vsphere.domain import api_state

    payload = await api_state.get_payload(
        database, "GET", "/api/vcenter/certificate-management/vcenter/tls"
    )
    return payload if isinstance(payload, dict) else {}


@router.get("/api/vcenter/certificate-management/vcenter/tls-csr")
async def machine_tls_csr_get(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, Any]:
    from app.vsphere.domain import api_state

    payload = await api_state.get_payload(
        database, "GET", "/api/vcenter/certificate-management/vcenter/tls-csr"
    )
    return payload if isinstance(payload, dict) else {}


@router.post("/api/vcenter/certificate-management/vcenter/tls-csr")
async def machine_tls_csr_create(
    body: dict[str, Any] | None = None,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_admin),
) -> dict[str, Any]:
    from app.vsphere.domain import api_state

    del body
    payload = await api_state.get_payload(
        database, "GET", "/api/vcenter/certificate-management/vcenter/tls-csr"
    )
    return payload if isinstance(payload, dict) else {}


@router.get("/api/vcenter/certificate-management/vcenter/trusted-root-chains")
async def trusted_root_chains(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    from app.vsphere.domain import api_state

    payload = await api_state.get_payload(
        database, "GET", "/api/vcenter/certificate-management/vcenter/trusted-root-chains"
    )
    return payload if isinstance(payload, list) else []


@router.get("/api/vcenter/vm/{vm}/guest/customization")
async def guest_customization_get(
    vm: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, Any]:
    obj = await vm_ops.require_vm(database, vm)
    customization = obj.props.get("customization")
    if isinstance(customization, dict) and customization:
        return customization
    # Seed / probe may wipe the field with POST {}; keep a non-empty lab view.
    return {
        "name": obj.name,
        "status": "PENDING",
        "spec": {"hostname": obj.name, "domain": "lab.local"},
    }


@router.get("/api/vcenter/vm/{vm}/guest/networking")
async def guest_networking(
    vm: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, Any]:
    return await vm_ops.guest_networking(database, vm)


@router.get("/api/vcenter/vm/{vm}/guest/power")
async def guest_power_get(
    vm: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, str]:
    obj = await vm_ops.require_vm(database, vm)
    state = str(obj.props.get("power_state") or "POWERED_OFF")
    return {"state": "RUNNING" if state == "POWERED_ON" else "NOT_RUNNING"}


@router.post("/api/vcenter/vm/{vm}/guest/power")
async def guest_power_post(
    vm: str,
    action: str = Query(...),
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("VirtualMachine.Interact.PowerOn")),
) -> Response:
    mapping = {"reboot": "reset", "shutdown": "stop", "standby": "suspend"}
    await vm_ops.set_power(database, vm, mapping.get(action, action))
    return Response(status_code=204)


@router.post("/api/vcenter/vm/{vm}/tools")
async def tools_upgrade(
    vm: str,
    action: str = Query("upgrade"),
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("VirtualMachine.Interact.DeviceConnection")),
) -> dict[str, str]:
    del action
    await vm_ops.require_vm(database, vm)
    from app.vsphere.domain import tasks as task_store

    task_id = await task_store.create_task(
        database,
        description=f"Upgrade tools {vm}",
        service="com.vmware.vcenter.vm.tools",
        operation="upgrade",
        result={"vm": vm},
    )
    return {"task": task_id}


@router.post("/api/vcenter/vm/{vm}/console/tickets")
async def console_tickets(
    vm: str,
    body: dict[str, Any] | None = None,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("VirtualMachine.Interact.ConsoleInteract")),
) -> dict[str, Any]:
    del body
    return await vm_ops.console_ticket(database, vm)


@router.post("/api/vcenter/vm/{vm}/guest/customization")
async def guest_customization(
    vm: str,
    body: dict[str, Any],
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("VirtualMachine.Config.Rename")),
) -> dict[str, str]:
    obj = await vm_ops.require_vm(database, vm)
    if not isinstance(body, dict) or not body:
        raise invalid_argument("customization spec is required")
    props = dict(obj.props)
    props["customization"] = body
    await inventory.update_props(database, vm, props)
    from app.vsphere.domain import tasks as task_store

    task_id = await task_store.create_task(
        database,
        description=f"Customize {vm}",
        service="com.vmware.vcenter.vm.guest.customization",
        operation="set",
        result={"vm": vm},
    )
    return {"task": task_id}


@router.get("/api/vcenter/vm/{vm}/guest/local-filesystem")
async def guest_local_filesystem(
    vm: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, Any]:
    obj = await vm_ops.require_vm(database, vm)
    filesystems = obj.props.get("guest_filesystems")
    if isinstance(filesystems, dict) and filesystems:
        return filesystems
    return {
        "filesystems": {
            "/": {"capacity": 42949672960, "free_space": 21474836480},
        }
    }

@router.get("/api/vcenter/vm/{vm}/guest/filesystem")
async def guest_filesystem_get(
    vm: str,
    path: str = Query("/"),
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, Any]:
    if path in {"/", "/tmp", "/etc"}:
        entries = await vm_ops.guest_list_files(database, vm, path)
        return {"path": path, "type": "DIRECTORY", "files": entries}
    content = await vm_ops.guest_read_file(database, vm, path)
    return {"path": path, "type": "FILE", "content": content, "size": len(content.encode("utf-8"))}


@router.put("/api/vcenter/vm/{vm}/guest/filesystem")
async def guest_filesystem_put(
    vm: str,
    body: dict[str, Any],
    path: str = Query(...),
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("VirtualMachine.Interact.DeviceConnection")),
) -> Response:
    content = str(body.get("content") or "")
    await vm_ops.guest_write_file(database, vm, path, content, overwrite=True)
    return Response(status_code=204)


@router.delete("/api/vcenter/vm/{vm}/guest/filesystem")
async def guest_filesystem_delete(
    vm: str,
    path: str = Query(...),
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("VirtualMachine.Interact.DeviceConnection")),
) -> Response:
    await vm_ops.guest_delete_file(database, vm, path)
    return Response(status_code=204)


@router.get("/api/vcenter/vm/{vm}/guest/filesystem/files")
async def guest_filesystem_list(
    vm: str,
    path: str = Query("/"),
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    return await vm_ops.guest_list_files(database, vm, path)


@router.post("/api/vcenter/vm/{vm}")
async def vm_action(
    vm: str,
    action: str = Query(...),
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("VirtualMachine.Provisioning.MarkAsTemplate")),
) -> dict[str, str]:
    if action == "mark-as-template":
        task_id = await vm_ops.set_template(database, vm, template=True)
    elif action in {"mark-as-vm", "mark-as-virtual-machine"}:
        task_id = await vm_ops.set_template(database, vm, template=False)
    elif action == "unregister":
        task_id = await vm_ops.unregister_vm(database, vm)
    else:
        raise invalid_argument(f"unsupported action {action}")
    return {"task": task_id}


@router.get("/api/vcenter/host/{host}/storage/storage-device")
async def host_storage(
    host: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    obj = await inventory.get_object(database, host)
    if obj is None or obj.type != "HostSystem":
        raise not_found(f"Host {host} not found")
    devices = obj.props.get("storage_devices")
    return list(devices) if isinstance(devices, list) else []


@router.get("/api/vcenter/host/{host}/networking")
async def host_networking(
    host: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, Any]:
    obj = await inventory.get_object(database, host)
    if obj is None or obj.type != "HostSystem":
        raise not_found(f"Host {host} not found")
    networking = obj.props.get("networking")
    return networking if isinstance(networking, dict) else {}


@router.get("/api/vcenter/folder/{folder}/children")
async def folder_children(
    folder: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, str]]:
    parent = await inventory.get_object(database, folder)
    if parent is None:
        raise not_found(f"Folder {folder} not found")
    children = [obj for obj in await inventory.list_objects(database) if obj.parent_moid == folder]
    return [{"moid": c.moid, "type": c.type, "name": c.name} for c in children]


@router.get("/api/vcenter/storage/policies/{policy}/vm")
async def policy_vms(
    policy: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[str]:
    objects = await inventory.list_objects(database, type_name="VirtualMachine")
    if policy in {"policy-default", "default"}:
        return [obj.moid for obj in objects]
    # Named non-default policies attach to every 10th VM for lab demos.
    return [obj.moid for i, obj in enumerate(objects) if i % 10 == 0]


@router.get("/api/vapi/metadata/metamodel/service")
async def metamodel_services(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[str]:
    from app.vsphere.domain import api_state

    payload = await api_state.get_payload(database, "GET", "/api/vapi/metadata/metamodel/service")
    return payload if isinstance(payload, list) else []


@router.get("/api/vapi/metadata/authentication/component")
async def authentication_components(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[str]:
    from app.vsphere.domain import api_state

    payload = await api_state.get_payload(
        database, "GET", "/api/vapi/metadata/authentication/component"
    )
    return payload if isinstance(payload, list) else []


@router.get("/api/vcenter/activity-history")
async def activity_history(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[dict[str, Any]]:
    from app.vsphere.domain import api_state

    payload = await api_state.get_payload(database, "GET", "/api/vcenter/activity-history")
    return payload if isinstance(payload, list) else []
