"""Route behaviour — full create→use→delete lifecycles against the fake provider.

Each mutating route 303-redirects to its list view; we assert the redirect AND
the resulting provider/list state, so the tests pin behaviour, not just status.
"""

from __future__ import annotations

import uvicorn
from fastapi.testclient import TestClient
from mgf.cloud.providers import get_provider

from mgf.cloud_web.web import _app

_VM = {"name": "t-vm", "flavor": "a2-ram2-disk20", "image": "ubuntu-24.04"}


def test_vm_lifecycle(client: TestClient) -> None:
    resp = client.post("/vms", data=_VM, follow_redirects=False)
    assert resp.status_code == 303
    assert resp.headers["location"] == "/vms"

    assert "t-vm" in client.get("/vms").text
    assert client.get("/vms/t-vm").status_code == 200

    assert client.post("/vms/t-vm/reboot", follow_redirects=False).status_code == 303
    assert client.post("/vms/t-vm/delete", follow_redirects=False).status_code == 303
    assert "t-vm" not in client.get("/vms").text


def test_volume_lifecycle_with_attach(client: TestClient) -> None:
    client.post("/vms", data=_VM, follow_redirects=False)
    assert (
        client.post(
            "/volumes", data={"name": "vol1", "size": "10"}, follow_redirects=False
        ).status_code
        == 303
    )
    assert "vol1" in client.get("/volumes").text

    assert (
        client.post(
            "/volumes/vol1/attach", data={"server": "t-vm"}, follow_redirects=False
        ).status_code
        == 303
    )
    assert (
        client.post(
            "/volumes/vol1/detach", data={"server": "t-vm"}, follow_redirects=False
        ).status_code
        == 303
    )
    assert client.post("/volumes/vol1/delete", follow_redirects=False).status_code == 303


def test_security_group_lifecycle(client: TestClient) -> None:
    assert (
        client.post(
            "/security-groups", data={"name": "sg1", "description": "web"}, follow_redirects=False
        ).status_code
        == 303
    )
    assert "sg1" in client.get("/security-groups").text
    assert client.post("/security-groups/sg1/delete", follow_redirects=False).status_code == 303


def test_keypair_lifecycle(client: TestClient) -> None:
    assert (
        client.post(
            "/keypairs",
            data={"name": "k1", "public_key": "ssh-ed25519 AAAAC3Nz test"},
            follow_redirects=False,
        ).status_code
        == 303
    )
    assert "k1" in client.get("/keypairs").text
    assert client.post("/keypairs/k1/delete", follow_redirects=False).status_code == 303


def test_floating_ip_lifecycle(client: TestClient, provider) -> None:  # type: ignore[no-untyped-def]
    assert (
        client.post("/floating-ips", data={"network": ""}, follow_redirects=False).status_code
        == 303
    )
    fips = provider.list_floating_ips()
    assert len(fips) == 1
    addr = fips[0].address

    assert client.post(f"/floating-ips/{addr}/release", follow_redirects=False).status_code == 303
    assert provider.list_floating_ips() == []


def test_object_storage_lifecycle(client: TestClient) -> None:
    assert client.post("/objects", data={"name": "c1"}, follow_redirects=False).status_code == 303
    assert "c1" in client.get("/objects").text
    assert client.get("/objects/c1").status_code == 200

    up = client.post(
        "/objects/c1/upload",
        files={"file": ("hello.txt", b"hi there", "text/plain")},
        follow_redirects=False,
    )
    assert up.status_code == 303

    dl = client.get("/objects/c1/hello.txt/download")
    assert dl.status_code == 200
    assert dl.content == b"hi there"

    assert client.post("/objects/c1/hello.txt/delete", follow_redirects=False).status_code == 303
    assert client.post("/objects/c1/delete", follow_redirects=False).status_code == 303


def test_serve_invokes_uvicorn(monkeypatch) -> None:  # type: ignore[no-untyped-def]
    """serve() wires bootstrap + uvicorn; cover it without binding a socket."""
    captured: dict[str, object] = {}
    monkeypatch.setattr(uvicorn, "run", lambda app, **kw: captured.update(kw))
    _app.serve(get_provider("fake"), host="127.0.0.1", port=0)
    assert captured["host"] == "127.0.0.1"
    assert captured["port"] == 0
