"""Worker semantics for backup/vzdump tasks."""

from __future__ import annotations

import json
from typing import Any

from app.simulation.clock import Clock
from app.simulation.seed import stable_id
from app.tasks.repository import Task, TaskRepository
from app.tasks.worker import TaskHandler


def backup_handler(repository: TaskRepository, clock: Clock) -> TaskHandler:
    async def execute(task: Task) -> dict[str, Any]:
        if task.task_type == "aptupdate":
            node = str(task.payload.get("node", "unknown"))
            await repository.append_log(task.id, f"starting apt update on {node}")
            await clock.sleep(1.0)
            async with repository.pool.acquire() as connection:
                metadata = await connection.fetchval(
                    "SELECT metadata FROM nodes WHERE name=$1",
                    node,
                )
                if metadata is not None:
                    payload = json.loads(metadata) if isinstance(metadata, str) else dict(metadata)
                    ops = payload.setdefault("ops", {})
                    apt = ops.setdefault("apt", {})
                    packages = list(apt.get("packages") or [])
                    for package in packages:
                        if isinstance(package, dict) and package.get("Status") == "upgradable":
                            package["Status"] = "installed"
                            if package.get("Version"):
                                package["OldVersion"] = package["Version"]
                    apt["packages"] = packages
                    apt["update"] = {"status": "stopped", "exitstatus": "OK"}
                    payload["ops"] = ops
                    await connection.execute(
                        "UPDATE nodes SET metadata=$2::jsonb, updated_at=now() WHERE name=$1",
                        node,
                        json.dumps(payload, sort_keys=True),
                    )
            await repository.append_log(task.id, "apt update finished")
            return {"status": "OK"}

        # Node/cluster ops whose durable side effects were applied by the API handler.
        if task.task_type != "vzdump":
            await repository.append_log(task.id, f"{task.task_type} completed")
            return {"status": "OK"}

        node = str(task.payload["node"])
        vmids = [str(item) for item in task.payload.get("vmids", [])]
        storage_id = str(task.payload.get("storage") or "nfs-backup")
        await repository.append_log(task.id, f"starting vzdump on {node} for {len(vmids)} guests")
        async with repository.pool.acquire() as connection:
            storage_resource_id = await connection.fetchval(
                "SELECT resource_id FROM storages WHERE storage_id=$1",
                storage_id,
            )
            if storage_resource_id is None:
                raise ValueError(f"storage {storage_id} does not exist")
            created = 0
            for index, vmid in enumerate(vmids):
                resource_id = await connection.fetchval(
                    """SELECT r.id FROM resources r JOIN nodes n ON n.id=r.node_id
                    WHERE n.name=$1 AND r.kind='qemu' AND r.external_id=$2""",
                    node,
                    vmid,
                )
                volume_id = f"backup/vzdump-qemu-{vmid}-{task.id.hex[:8]}-{index:04d}.vma.zst"
                await connection.execute(
                    """INSERT INTO backups(
                        id, resource_id, storage_resource_id, volume_id, size_bytes, metadata
                    ) VALUES($1, $2, $3, $4, $5, $6::jsonb)
                    ON CONFLICT (storage_resource_id, volume_id) DO NOTHING""",
                    stable_id(f"backup-task:{task.id}:{vmid}"),
                    resource_id,
                    storage_resource_id,
                    volume_id,
                    (8 + index) * 1024**3,
                    json.dumps(
                        {"mode": task.payload.get("mode", "snapshot"), "type": "vzdump"},
                        sort_keys=True,
                    ),
                )
                created += 1
                await repository.append_log(task.id, f"backup archive created: {volume_id}")
        await repository.append_log(task.id, f"vzdump finished ({created} archives)")
        return {"created": created}

    return execute
