"""Phase 17 stub-routes smoke tests.

Verifies the /api/queue, /api/chat/*, /api/slash-commands, /api/commands-ref
GETs return fixture-shaped data, and that the mutation POSTs return
{"ok": true} and update the in-memory acted-on map (so the queue shrinks
after approve/reject/defer).
"""

from __future__ import annotations

import pytest
from fastapi.testclient import TestClient

from recoil.api.main import app
from recoil.api.stub_routes import _reset_acted_for_tests


@pytest.fixture
def client() -> TestClient:
    _reset_acted_for_tests()
    return TestClient(app)


# ── GETs ────────────────────────────────────────────────────────────────────
def test_queue_returns_pending_jobs_recent(client: TestClient) -> None:
    r = client.get("/api/queue")
    assert r.status_code == 200
    body = r.json()
    assert set(body.keys()) == {"pending", "jobs", "recent", "stub_mode"}
    assert isinstance(body["pending"], list)
    assert len(body["pending"]) == 8  # one per Phase 11 proposal kind
    # Each item is a PendingItem-shaped dict.
    item = body["pending"][0]
    assert {"id", "ts", "kind", "from", "proposal", "schemaVersion"} <= set(item.keys())
    assert item["proposal"]["kind"] == item["kind"]
    # Live jobs: 7 jobs minimum.
    assert len(body["jobs"]) >= 6
    # Recent: 10+ historical outcomes.
    assert len(body["recent"]) >= 10


def test_queue_pending_has_eight_canonical_kinds(client: TestClient) -> None:
    r = client.get("/api/queue")
    pending = r.json()["pending"]
    kinds = {p["kind"] for p in pending}
    assert kinds == {
        "PromptRewriteProposal",
        "BeatInsertionProposal",
        "ParameterChangeProposal",
        "ScriptEditProposal",
        "MultiBeatDirectiveProposal",
        "ExtractCutawayProposal",
        "RefSwapProposal",
        "RetryStrategyEditProposal",
    }


def test_chat_history_returns_thread(client: TestClient) -> None:
    r = client.get("/api/chat/tartarus")
    assert r.status_code == 200
    history = r.json()
    assert isinstance(history, list)
    assert len(history) > 0
    roles = {turn["role"] for turn in history}
    assert {"system", "user", "assistant"} <= roles


def test_chat_history_works_for_any_project_id(client: TestClient) -> None:
    # Phase 17 ignores project_id (returns the same fixture). Phase 19+
    # makes this per-project. Test guards Phase 17 behaviour.
    r = client.get("/api/chat/recoil-otf-demo")
    assert r.status_code == 200
    assert isinstance(r.json(), list)


def test_chat_context_window_shape(client: TestClient) -> None:
    r = client.get("/api/chat/context-window")
    assert r.status_code == 200
    body = r.json()
    assert {"used", "max", "breakdown"} <= set(body.keys())
    assert isinstance(body["breakdown"], list)


def test_slash_commands_list(client: TestClient) -> None:
    r = client.get("/api/slash-commands")
    assert r.status_code == 200
    cmds = r.json()
    assert isinstance(cmds, list)
    assert len(cmds) > 0
    # Each command has cmd / desc / group.
    cmd = cmds[0]
    assert {"cmd", "desc", "group"} <= set(cmd.keys())


def test_commands_ref_grouped(client: TestClient) -> None:
    r = client.get("/api/commands-ref")
    assert r.status_code == 200
    ref = r.json()
    assert isinstance(ref, dict)
    # Each top-level key maps to a list of {cmd, short, desc} entries.
    for group_key, entries in ref.items():
        assert isinstance(group_key, str)
        assert isinstance(entries, list)
        for e in entries:
            assert "cmd" in e and "desc" in e


# ── POST mutations ──────────────────────────────────────────────────────────
def test_approve_proposal_returns_ok_true(client: TestClient) -> None:
    r = client.post("/api/proposals/prop_001/approve")
    assert r.status_code == 200
    assert r.json() == {"ok": True}


def test_approve_proposal_removes_it_from_pending(client: TestClient) -> None:
    pre = client.get("/api/queue").json()["pending"]
    pre_ids = {p["id"] for p in pre}
    assert "prop_002" in pre_ids
    client.post("/api/proposals/prop_002/approve")
    post = client.get("/api/queue").json()["pending"]
    post_ids = {p["id"] for p in post}
    assert "prop_002" not in post_ids


def test_reject_proposal_returns_ok_true(client: TestClient) -> None:
    assert client.post("/api/proposals/prop_003/reject").json() == {"ok": True}


def test_defer_proposal_returns_ok_true(client: TestClient) -> None:
    assert client.post("/api/proposals/prop_004/defer").json() == {"ok": True}


def test_mark_primary_take(client: TestClient) -> None:
    assert client.post("/api/takes/b5_t9/mark-primary").json() == {"ok": True}


def test_mark_circled_take_toggles(client: TestClient) -> None:
    # First call: ok=True. Second call: still ok=True (just toggles state).
    assert client.post("/api/takes/b5_t3/mark-circled").json() == {"ok": True}
    assert client.post("/api/takes/b5_t3/mark-circled").json() == {"ok": True}


def test_reject_take_returns_ok_true(client: TestClient) -> None:
    assert client.post("/api/takes/b5_t7/reject").json() == {"ok": True}


def test_toggle_memory_returns_ok_true(client: TestClient) -> None:
    assert client.post("/api/memory/m_001/toggle").json() == {"ok": True}
