"""hive — the swarm address book service (see hive/app.py).

The app module reads env config at import time, so each test reimports it with
fresh env into its own key file / database.
"""

import importlib
import sqlite3

import pytest
from fastapi.testclient import TestClient


@pytest.fixture()
def hive(tmp_path, monkeypatch):
    monkeypatch.setenv("HIVE_KEY_FILE", str(tmp_path / ".swarm-key"))
    monkeypatch.setenv("HIVE_DB", str(tmp_path / "hive.db"))
    monkeypatch.setenv("HIVE_TTL_SEC", "300")
    (tmp_path / ".swarm-key").write_text("secret-key-123")
    import hive.app as hive_app

    module = importlib.reload(hive_app)
    yield module
    module._db.close()


def _headers(key: str = "secret-key-123") -> dict:
    return {"X-Swarm-Key": key}


def _announce(client, name="quiet-otter", instance_id="11111111-2222-3333-4444-555555555555", port=8099):
    return client.post(
        "/announce",
        json={"name": name, "instance_id": instance_id, "version": "0.1.0", "port": port,
              "meta": {"hostname": "gpu-box", "os": "Linux 6.1", "cpu_cores": 16, "ram_mb": 65536}},
        headers=_headers(),
    )


def test_health_open_without_key(hive):
    with TestClient(hive.app) as client:
        resp = client.get("/health")
        assert resp.status_code == 200
        assert resp.json()["hive"] is True


def test_announce_requires_key(hive):
    with TestClient(hive.app) as client:
        assert client.post("/announce", json={"name": "x", "instance_id": "12345678", "port": 8099}).status_code == 401
        assert client.post(
            "/announce",
            json={"name": "x", "instance_id": "12345678", "port": 8099},
            headers={"X-Swarm-Key": "wrong"},
        ).status_code == 403


def test_peers_requires_key(hive):
    with TestClient(hive.app) as client:
        assert client.get("/peers").status_code == 401
        assert client.get("/peers", headers={"X-Swarm-Key": "wrong"}).status_code == 403


def test_announce_upsert_and_peers_listing(hive):
    with TestClient(hive.app) as client:
        assert _announce(client).status_code == 200
        # same instance_id again = upsert (renamed, different port), not a second row
        assert _announce(client, name="renamed-otter", port=8123).status_code == 200
        # a second machine
        assert _announce(client, name="amber-falcon", instance_id="99999999-2222-3333-4444-555555555555").status_code == 200

        resp = client.get("/peers", headers=_headers())
        assert resp.status_code == 200
        body = resp.json()
        peers = body["peers"]
        assert len(peers) == 2
        assert [p["name"] for p in peers] == ["amber-falcon", "renamed-otter"]  # sorted by name
        otter = peers[1]
        assert otter["port"] == 8123
        assert otter["address"] == f"{otter['host']}:8123"
        assert otter["online"] is True
        assert otter["meta"]["hostname"] == "gpu-box"
        assert otter["meta"]["ram_mb"] == 65536
        assert otter["version"] == "0.1.0"
        assert body["ttl_sec"] == 300


def test_offline_after_ttl(hive, tmp_path):
    with TestClient(hive.app) as client:
        _announce(client)
        # backdate the last announcement beyond the TTL — machine went quiet
        conn = sqlite3.connect(tmp_path / "hive.db")
        conn.execute("UPDATE machines SET last_seen = last_seen - 400")
        conn.commit()
        conn.close()
        peers = client.get("/peers", headers=_headers()).json()["peers"]
        assert peers[0]["online"] is False


def test_previous_key_still_accepted(hive, tmp_path):
    # rotation window: .swarm-key.previous keeps working
    (tmp_path / ".swarm-key.previous").write_text("old-key-42")
    import hive.app as module

    importlib.reload(module)
    with TestClient(module.app) as client:
        resp = client.post(
            "/announce",
            json={"name": "x", "instance_id": "12345678", "port": 8099},
            headers={"X-Swarm-Key": "old-key-42"},
        )
        assert resp.status_code == 200
    module._db.close()


def test_hive_without_key_file_refuses(hive, tmp_path, monkeypatch):
    monkeypatch.setenv("HIVE_KEY_FILE", str(tmp_path / ".no-key"))
    monkeypatch.setenv("HIVE_DB", str(tmp_path / "hive2.db"))
    import hive.app as module

    importlib.reload(module)
    with TestClient(module.app) as client:
        resp = client.post(
            "/announce",
            json={"name": "x", "instance_id": "12345678", "port": 8099},
            headers={"X-Swarm-Key": "whatever"},
        )
        assert resp.status_code == 503
    module._db.close()