"""Cluster resource mapping handlers (dir/pci/usb)."""

from __future__ import annotations

from typing import Any

from fastapi import Request

from app.api.errors import ApiError
from app.api.registry import HandlerRegistry
from app.handlers.common import cluster_metadata, save_cluster_metadata, subdirs, values


def _mappings(metadata: dict[str, Any]) -> dict[str, Any]:
    current = metadata.get("mapping")
    if not isinstance(current, dict):
        return {"dir": {}, "pci": {}, "usb": {}}
    return {
        "dir": dict(current["dir"]) if isinstance(current.get("dir"), dict) else {},
        "pci": dict(current["pci"]) if isinstance(current.get("pci"), dict) else {},
        "usb": dict(current["usb"]) if isinstance(current.get("usb"), dict) else {},
    }


def register_mapping_handlers(registry: HandlerRegistry) -> None:
    async def index(_request: Request, _inputs: dict[str, Any]) -> list[dict[str, str]]:
        return subdirs("dir", "pci", "usb")

    def register_kind(kind: str) -> None:
        base = f"/cluster/mapping/{kind}"

        async def list_items(request: Request, inputs: dict[str, Any]) -> list[dict[str, Any]]:
            metadata = await cluster_metadata(request)
            store = _mappings(metadata)[kind]
            check_node = values(inputs).get("check-node")
            result = [{"id": key, **item} for key, item in sorted(store.items())]
            if check_node:
                for item in result:
                    item["checks"] = {str(check_node): "OK"}
            return result

        async def create(request: Request, inputs: dict[str, Any]) -> None:
            payload = values(inputs)
            item_id = str(payload["id"])
            metadata = await cluster_metadata(request)
            mapping = _mappings(metadata)
            store = mapping[kind]
            if item_id in store:
                raise ApiError(400, f"{kind} mapping '{item_id}' already exists")
            store[item_id] = {
                key: value for key, value in payload.items() if key not in {"delete", "digest"}
            }
            metadata["mapping"] = mapping
            await save_cluster_metadata(request, metadata)

        async def get(request: Request, inputs: dict[str, Any]) -> dict[str, Any]:
            item_id = str(values(inputs)["id"])
            metadata = await cluster_metadata(request)
            store = _mappings(metadata)[kind]
            if item_id not in store:
                raise ApiError(404, f"{kind} mapping does not exist")
            return {"id": item_id, **store[item_id]}

        async def update(request: Request, inputs: dict[str, Any]) -> None:
            payload = values(inputs)
            item_id = str(payload["id"])
            metadata = await cluster_metadata(request)
            mapping = _mappings(metadata)
            store = mapping[kind]
            if item_id not in store:
                raise ApiError(404, f"{kind} mapping does not exist")
            current = dict(store[item_id])
            for key in [
                item.strip() for item in str(payload.get("delete") or "").split(",") if item.strip()
            ]:
                current.pop(key, None)
            for key, value in payload.items():
                if key in {"id", "delete", "digest"}:
                    continue
                current[key] = value
            current["id"] = item_id
            store[item_id] = current
            metadata["mapping"] = mapping
            await save_cluster_metadata(request, metadata)

        async def delete(request: Request, inputs: dict[str, Any]) -> None:
            item_id = str(values(inputs)["id"])
            metadata = await cluster_metadata(request)
            mapping = _mappings(metadata)
            store = mapping[kind]
            if item_id not in store:
                raise ApiError(404, f"{kind} mapping does not exist")
            del store[item_id]
            metadata["mapping"] = mapping
            await save_cluster_metadata(request, metadata)

        registry.register(base, "GET", list_items)
        registry.register(base, "POST", create)
        registry.register(f"{base}/{{id}}", "GET", get)
        registry.register(f"{base}/{{id}}", "PUT", update)
        registry.register(f"{base}/{{id}}", "DELETE", delete)

    registry.register("/cluster/mapping", "GET", index)
    register_kind("dir")
    register_kind("pci")
    register_kind("usb")
