"""Tests for the mgf-cloud web GUI (Pass 5), via FastAPI TestClient + FakeProvider."""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest
from fastapi.testclient import TestClient

from mgf.cloud.providers.fake import FakeProvider
from mgf.cloud.web import create_app

if TYPE_CHECKING:
    from collections.abc import Iterator


@pytest.fixture
def provider() -> FakeProvider:
    return FakeProvider()


@pytest.fixture
def client(provider: FakeProvider) -> Iterator[TestClient]:
    with TestClient(create_app(provider)) as test_client:
        yield test_client


def test_dashboard(client: TestClient) -> None:
    resp = client.get("/")
    assert resp.status_code == 200
    assert "mgf-cloud" in resp.text


def test_read_pages(client: TestClient) -> None:
    for path in (
        "/vms",
        "/vms/new",
        "/volumes",
        "/floating-ips",
        "/security-groups",
        "/keypairs",
        "/objects",
    ):
        assert client.get(path).status_code == 200, path


def test_vm_new_form_lists_flavors(client: TestClient) -> None:
    assert "a2-ram2-disk20" in client.get("/vms/new").text


def test_vm_create_detail_reboot_delete(client: TestClient, provider: FakeProvider) -> None:
    resp = client.post(
        "/vms",
        data={
            "name": "web",
            "flavor": "a2-ram2-disk20",
            "image": "Ubuntu 24.04 LTS",
            "floating_ip": "true",
        },
        follow_redirects=False,
    )
    assert resp.status_code == 303
    assert provider.get_server("web").floating_ips  # created + got a floating IP

    assert "web" in client.get("/vms").text
    assert client.get("/vms/web").status_code == 200

    assert client.post("/vms/web/reboot", follow_redirects=False).status_code == 303
    assert client.post("/vms/web/delete", follow_redirects=False).status_code == 303
    assert provider.list_servers() == []


def test_missing_vm_renders_html_error_404(client: TestClient) -> None:
    resp = client.get("/vms/ghost")
    assert resp.status_code == 404
    assert "Error 404" in resp.text


def test_volume_create_and_delete(client: TestClient, provider: FakeProvider) -> None:
    assert (
        client.post(
            "/volumes", data={"name": "data", "size": "50"}, follow_redirects=False
        ).status_code
        == 303
    )
    assert provider.get_volume("data").size_gb == 50
    assert "data" in client.get("/volumes").text
    assert client.post("/volumes/data/delete", follow_redirects=False).status_code == 303


def test_floating_ip_create_and_release(client: TestClient, provider: FakeProvider) -> None:
    assert (
        client.post(
            "/floating-ips", data={"network": "ext-net1"}, follow_redirects=False
        ).status_code
        == 303
    )
    addr = provider.list_floating_ips()[0].address
    assert client.post(f"/floating-ips/{addr}/release", follow_redirects=False).status_code == 303
    assert provider.list_floating_ips() == []


def test_web_object_storage_management(client: TestClient, provider: FakeProvider) -> None:
    assert (
        client.post("/objects", data={"name": "backups"}, follow_redirects=False).status_code == 303
    )
    assert "backups" in client.get("/objects").text
    assert client.get("/objects/backups").status_code == 200
    up = client.post(
        "/objects/backups/upload",
        files={"file": ("a.txt", b"hello", "text/plain")},
        follow_redirects=False,
    )
    assert up.status_code == 303
    assert provider.get_object("backups", "a.txt") == b"hello"
    assert "a.txt" in client.get("/objects/backups").text
    dl = client.get("/objects/backups/a.txt/download")
    assert dl.status_code == 200
    assert dl.content == b"hello"
    assert client.post("/objects/backups/a.txt/delete", follow_redirects=False).status_code == 303
    assert client.post("/objects/backups/delete", follow_redirects=False).status_code == 303
    assert provider.list_containers() == []


def test_web_secgroup_create_delete(client: TestClient, provider: FakeProvider) -> None:
    resp = client.post(
        "/security-groups", data={"name": "ci", "description": "ci hosts"}, follow_redirects=False
    )
    assert resp.status_code == 303
    assert "ci" in client.get("/security-groups").text
    assert client.post("/security-groups/ci/delete", follow_redirects=False).status_code == 303
    assert provider.list_security_groups() == []


def test_web_keypair_create_delete(client: TestClient, provider: FakeProvider) -> None:
    resp = client.post(
        "/keypairs", data={"name": "k1", "public_key": "ssh-ed25519 AAAA"}, follow_redirects=False
    )
    assert resp.status_code == 303
    assert "k1" in client.get("/keypairs").text
    assert client.post("/keypairs/k1/delete", follow_redirects=False).status_code == 303
    assert provider.list_keypairs() == []


def test_web_volume_attach_detach(client: TestClient, provider: FakeProvider) -> None:
    client.post(
        "/vms", data={"name": "vm", "flavor": "a2-ram2-disk20", "image": "Ubuntu 24.04 LTS"}
    )
    client.post("/volumes", data={"name": "data", "size": "10"})
    assert (
        client.post(
            "/volumes/data/attach", data={"server": "vm"}, follow_redirects=False
        ).status_code
        == 303
    )
    assert provider.get_volume("data").attached_server_id is not None
    assert (
        client.post(
            "/volumes/data/detach", data={"server": "vm"}, follow_redirects=False
        ).status_code
        == 303
    )
    assert provider.get_volume("data").attached_server_id is None


def test_cli_web_command_invokes_serve(monkeypatch: pytest.MonkeyPatch) -> None:
    from mgf.cloud.__main__ import main

    captured: dict[str, object] = {}

    def fake_serve(prov: object, host: str, port: int) -> None:
        captured["host"] = host
        captured["port"] = port

    monkeypatch.setattr("mgf.cloud.web.serve", fake_serve)
    assert main(["web", "--provider", "fake", "--host", "127.0.0.1", "--port", "9999"]) == 0
    assert captured == {"host": "127.0.0.1", "port": 9999}
