"""HTTP-boundary API-token and contract permission tests."""

from typing import Any, cast

import pytest
from fastapi import FastAPI, Request

from app.api.errors import ApiError
from app.api.registry import _authenticate
from app.config import Settings
from app.contracts.model import Method, Permissions, Schema
from app.db.pool import AsyncpgDatabase
from app.security.auth import hash_secret


class FakePool:
    def __init__(self, secret: str, token_privileges: list[str]) -> None:
        self.secret_hash = hash_secret(secret, salt=b"boundary-token-v1")
        self.token_privileges = token_privileges

    async def fetchrow(self, _query: str, principal: str, token_id: str) -> dict[str, Any] | None:
        if principal != "operator@pve" or token_id != "api":
            return None
        return {
            "name": principal,
            "secret_hash": self.secret_hash,
            "privileges": self.token_privileges,
            "privilege_separation": True,
        }

    async def fetch(self, _query: str, principal: str) -> list[dict[str, Any]]:
        return [
            {
                "path": "/vms",
                "propagate": True,
                "privileges": ["VM.Audit", "VM.PowerMgmt"],
                "principal": principal,
            }
        ]


class FakeDatabase:
    def __init__(self, pool: FakePool) -> None:
        self.pool = pool


def token_request(secret: str, token_privileges: list[str]) -> Request:
    app = FastAPI()
    app.state.settings = Settings()
    app.state.database = cast(AsyncpgDatabase, FakeDatabase(FakePool("valid", token_privileges)))
    return Request(
        {
            "type": "http",
            "app": app,
            "method": "POST",
            "path": "/api2/json/nodes/pve1/qemu/101/status/start",
            "headers": [(b"authorization", f"PVEAPIToken=operator@pve!api={secret}".encode())],
            "query_string": b"",
            "server": ("test", 80),
            "client": ("test", 123),
            "scheme": "http",
        }
    )


def power_method() -> Method:
    return Method(
        verb="POST",
        name="start",
        returns=Schema(type="string"),
        permissions=Permissions(expression={"check": ["perm", "/vms/{vmid}", ["VM.PowerMgmt"]]}),
        checksum="1" * 64,
    )


async def test_api_token_skips_csrf_but_honors_separated_privileges() -> None:
    allowed = token_request("valid", ["VM.PowerMgmt"])
    await _authenticate(
        allowed,
        "/nodes/{node}/qemu/{vmid}/status/start",
        power_method(),
        {"values": {"node": "pve1", "vmid": 101}},
    )
    assert allowed.state.principal == "operator@pve"

    denied = token_request("valid", ["VM.Audit"])
    with pytest.raises(ApiError) as error:
        await _authenticate(
            denied,
            "/nodes/{node}/qemu/{vmid}/status/start",
            power_method(),
            {"values": {"node": "pve1", "vmid": 101}},
        )
    assert error.value.status_code == 403


async def test_api_token_rejects_unknown_or_wrong_secret() -> None:
    request = token_request("wrong", ["VM.PowerMgmt"])
    with pytest.raises(ApiError) as error:
        await _authenticate(
            request,
            "/nodes/{node}/qemu/{vmid}/status/start",
            power_method(),
            {"values": {"node": "pve1", "vmid": 101}},
        )
    assert error.value.status_code == 401
