"""Cluster backup job handlers."""

from __future__ import annotations

import json
from typing import Any, cast

from fastapi import FastAPI, Request

from app.api.registry import HandlerRegistry
from app.db.pool import AsyncpgDatabase
from app.handlers.backup import register_backup_handlers


class BackupPool:
    def __init__(self) -> None:
        self.metadata: dict[str, Any] = {
            "backup_jobs": {
                "backup-daily": {
                    "id": "backup-daily",
                    "schedule": "0 2 * * *",
                    "storage": "local",
                    "enabled": 1,
                    "vmid": "100",
                }
            }
        }
        self.guests = [
            {
                "external_id": "100",
                "kind": "qemu",
                "state": '{"name":"demo"}',
                "qemu_config": '{"scsi0":"local-lvm:vm-100-disk-0,size=32G"}',
                "lxc_config": None,
            },
            {
                "external_id": "200",
                "kind": "lxc",
                "state": '{"name":"service"}',
                "qemu_config": None,
                "lxc_config": '{"rootfs":"local-lvm:vm-200-disk-0,size=8G"}',
            },
        ]

    async def fetchrow(self, query: str, *_arguments: object) -> dict[str, Any] | None:
        if "FROM clusters WHERE id" in query:
            return {"metadata": json.dumps(self.metadata)}
        raise AssertionError(query)

    async def fetch(self, query: str, *arguments: object) -> list[dict[str, Any]]:
        if "FROM resources r" in query and "ANY($1" in query:
            selected = set(str(item) for item in cast(list[Any], arguments[0]))
            return [row for row in self.guests if row["external_id"] in selected]
        if "FROM resources r" in query:
            return list(self.guests)
        raise AssertionError(query)

    async def execute(self, query: str, *arguments: object) -> str:
        if "UPDATE clusters SET metadata" in query:
            self.metadata = json.loads(str(arguments[1]))
            return "UPDATE 1"
        raise AssertionError(query)


def _request(pool: BackupPool) -> Request:
    app = FastAPI()
    app.state.database = cast(AsyncpgDatabase, type("DB", (), {"pool": pool})())
    return Request(
        {
            "type": "http",
            "app": app,
            "method": "POST",
            "path": "/",
            "headers": [],
            "query_string": b"",
            "server": ("test", 80),
            "client": ("test", 123),
            "scheme": "http",
        }
    )


async def test_backup_jobs_crud_and_shapes() -> None:
    registry = HandlerRegistry()
    register_backup_handlers(registry)
    pool = BackupPool()
    http = _request(pool)

    list_jobs = registry.get("/cluster/backup", "GET")
    create_job = registry.get("/cluster/backup", "POST")
    backup_info = registry.get("/cluster/backup-info", "GET")
    not_backed_up = registry.get("/cluster/backup-info/not-backed-up", "GET")
    included_volumes = registry.get("/cluster/backup/{id}/included_volumes", "GET")
    delete_job = registry.get("/cluster/backup/{id}", "DELETE")
    assert list_jobs is not None
    assert create_job is not None
    assert backup_info is not None
    assert not_backed_up is not None
    assert included_volumes is not None
    assert delete_job is not None

    listed = await list_jobs(http, {"values": {}})
    assert listed[0]["id"] == "backup-daily"
    assert listed[0]["schedule"] == "0 2 * * *"

    await create_job(
        http,
        {
            "values": {
                "id": "backup-weekly",
                "schedule": "@weekly",
                "storage": "local",
                "vmid": "200",
            }
        },
    )
    assert "backup-weekly" in pool.metadata["backup_jobs"]

    info = await backup_info(http, {"values": {}})
    assert info == [{"subdir": "not-backed-up"}]

    missing = await not_backed_up(http, {"values": {}})
    # daily covers 100, weekly covers 200 → none missing
    assert missing == []

    included = await included_volumes(http, {"values": {"id": "backup-daily"}})
    assert included["children"][0]["id"] == 100
    assert included["children"][0]["children"][0]["id"] == "scsi0"

    await delete_job(http, {"values": {"id": "backup-weekly"}})
    assert "backup-weekly" not in pool.metadata["backup_jobs"]
    missing_after = await not_backed_up(http, {"values": {}})
    assert missing_after[0]["vmid"] == 200
    assert missing_after[0]["type"] == "lxc"
