"""CIS tagging REST."""

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.domain import tagging
from app.vsphere.errors import invalid_argument
from app.vsphere.security.authz import require_privilege, require_read
from app.vsphere.security.session import SessionInfo

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


@router.get("/api/cis/tagging/category")
async def list_categories(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[str]:
    return await tagging.list_categories(database)


@router.post("/api/cis/tagging/category")
async def create_category(
    body: dict[str, Any],
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("InventoryService.Tagging.CreateCategory")),
) -> str:
    spec = body.get("create_spec") if isinstance(body.get("create_spec"), dict) else body
    name = spec.get("name") if isinstance(spec, dict) else None
    if not name:
        raise invalid_argument("create_spec.name is required")
    return await tagging.create_category(
        database,
        name=str(name),
        description=str(spec.get("description") or ""),
        cardinality=str(spec.get("cardinality") or "MULTIPLE"),
        associable_types=list(spec.get("associable_types") or []),
    )


@router.get("/api/cis/tagging/category/{category_id}")
async def get_category(
    category_id: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, Any]:
    return await tagging.get_category(database, category_id)


@router.delete("/api/cis/tagging/category/{category_id}")
async def delete_category(
    category_id: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("InventoryService.Tagging.CreateCategory")),
) -> Response:
    await tagging.delete_category(database, category_id)
    return Response(status_code=204)


@router.get("/api/cis/tagging/tag")
async def list_tags(
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> list[str]:
    return await tagging.list_tags(database)


@router.post("/api/cis/tagging/tag")
async def create_tag(
    body: dict[str, Any],
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("InventoryService.Tagging.CreateTag")),
) -> str:
    spec = body.get("create_spec") if isinstance(body.get("create_spec"), dict) else body
    if not isinstance(spec, dict):
        raise invalid_argument("create_spec is required")
    category_id = spec.get("category_id")
    name = spec.get("name")
    if not category_id:
        raise invalid_argument("create_spec.category_id is required")
    if not name:
        raise invalid_argument("create_spec.name is required")
    return await tagging.create_tag(
        database,
        category_id=str(category_id),
        name=str(name),
        description=str(spec.get("description") or ""),
    )


@router.get("/api/cis/tagging/tag/{tag_id}")
async def get_tag(
    tag_id: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_read),
) -> dict[str, Any]:
    return await tagging.get_tag(database, tag_id)


@router.delete("/api/cis/tagging/tag/{tag_id}")
async def delete_tag(
    tag_id: str,
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("InventoryService.Tagging.CreateTag")),
) -> Response:
    await tagging.delete_tag(database, tag_id)
    return Response(status_code=204)


@router.post("/api/cis/tagging/tag-association")
async def tag_association(
    body: dict[str, Any],
    action: str | None = Query(None),
    database: Database = Depends(get_database),
    _: SessionInfo = Depends(require_privilege("InventoryService.Tagging.AttachTag")),
) -> Any:
    # Official Automation uses ?action=attach|detach|list-attached-tags.
    # Legacy /rest uses ?~action=…; callers may also put action in the JSON body.
    resolved = str(action or body.get("action") or "attach").strip().lower()
    tag_id = body.get("tag_id")
    obj = body.get("object_id") or body.get("object") or {}
    if not isinstance(obj, dict):
        obj = {}
    object_type = str(obj.get("type") or body.get("type") or "VirtualMachine")
    object_id = str(obj.get("id") or body.get("id") or "")
    if resolved in {"list-attached-tags", "list-attached-tags-on-objects"}:
        if not object_id:
            raise invalid_argument("object_id.id is required")
        return await tagging.list_attached_tags(database, object_type, object_id)
    if not tag_id:
        raise invalid_argument("tag_id is required")
    if not object_id:
        raise invalid_argument("object_id.id is required")
    if resolved == "attach":
        await tagging.attach_tag(database, str(tag_id), object_type, object_id)
        return Response(status_code=204)
    if resolved == "detach":
        await tagging.detach_tag(database, str(tag_id), object_type, object_id)
        return Response(status_code=204)
    raise invalid_argument(f"unsupported action {resolved}")
