"""BDD tests for runtime/api/chat.py.

Given: compiled test-persona fixture package (personas/examples/test-persona)
When:  user sends a message about a corpus topic
Then:  streamed SSE reply contains relevant persona content (via FakeProvider)
"""

from __future__ import annotations

import json
import shutil

import pytest
import yaml
from fastapi.testclient import TestClient


@pytest.fixture
def client(examples_persona_root, monkeypatch):
    """TestClient with FakeProvider and examples/ as persona root."""
    monkeypatch.setenv("FAKE_PROVIDER", "1")
    monkeypatch.setenv("MENTORCORE_PERSONAS_ROOT", str(examples_persona_root))
    # Re-import app after env is set so providers resolve correctly
    from runtime.api.app import app

    return TestClient(app)


# ── GET /api/personas ─────────────────────────────────────────────────────────


def test_default_personas_root_points_at_project_personas(monkeypatch):
    monkeypatch.delenv("MENTORCORE_PERSONAS_ROOT", raising=False)
    import runtime.api.chat as chat_module

    root = chat_module._personas_root()
    assert root.name == "personas"
    assert root.parent.name == "MentorCore"


def test_list_personas_returns_test_persona(client):
    """Given: examples/ has test-persona. When: GET /api/personas. Then: test-persona listed."""
    resp = client.get("/api/personas")
    assert resp.status_code == 200
    slugs = [p["slug"] for p in resp.json()]
    assert "test-persona" in slugs


def test_list_personas_has_display_name(client):
    resp = client.get("/api/personas")
    personas = {p["slug"]: p for p in resp.json()}
    assert personas["test-persona"]["display_name"] == "Test Persona"


# ── POST /api/chat/{slug} ─────────────────────────────────────────────────────


def _parse_sse(body: str) -> list[dict]:
    events = []
    for line in body.splitlines():
        if line.startswith("data: "):
            events.append(json.loads(line[6:]))
    return events


def test_chat_streams_sse(client):
    """Given: test-persona. When: POST /api/chat/test-persona. Then: SSE stream received."""
    resp = client.post(
        "/api/chat/test-persona",
        json={"message": "Tell me about virtue."},
    )
    assert resp.status_code == 200
    assert "text/event-stream" in resp.headers["content-type"]


def test_chat_reply_contains_text_events(client):
    resp = client.post(
        "/api/chat/test-persona",
        json={"message": "What do you say about virtue?"},
    )
    events = _parse_sse(resp.text)
    text_events = [e for e in events if e.get("type") == "text"]
    assert len(text_events) > 0


def test_chat_reply_ends_with_done(client):
    resp = client.post(
        "/api/chat/test-persona",
        json={"message": "Hello"},
    )
    events = _parse_sse(resp.text)
    assert events[-1]["type"] == "done"


def test_chat_returns_session_id(client):
    resp = client.post("/api/chat/test-persona", json={"message": "Hello"})
    events = _parse_sse(resp.text)
    session_events = [e for e in events if e.get("type") == "session"]
    assert len(session_events) == 1
    assert "session_id" in session_events[0]


def test_chat_multiturn_reuses_session(client):
    """Given: session_id from turn 1. When: turn 2 sends same session_id. Then: accepted."""
    resp1 = client.post("/api/chat/test-persona", json={"message": "Hello"})
    events1 = _parse_sse(resp1.text)
    session_id = next(e["session_id"] for e in events1 if e.get("type") == "session")

    resp2 = client.post(
        "/api/chat/test-persona",
        json={"message": "Continue.", "session_id": session_id},
    )
    assert resp2.status_code == 200
    events2 = _parse_sse(resp2.text)
    assert any(e.get("type") == "done" for e in events2)


def test_chat_unknown_persona_returns_404(client):
    resp = client.post("/api/chat/nonexistent", json={"message": "Hi"})
    assert resp.status_code == 404


def test_chat_uses_manifest_retrieval_top_k(test_persona_dir, tmp_path, monkeypatch):
    root = tmp_path / "personas"
    shutil.copytree(test_persona_dir, root / "test-persona")
    manifest_path = root / "test-persona" / "persona.yaml"
    manifest = yaml.safe_load(manifest_path.read_text())
    manifest["retrieval_top_k"] = 8
    manifest_path.write_text(yaml.dump(manifest))

    observed = {}

    def fake_retrieve(query, index_dir, *, top_k=5, embedder=None):
        observed["top_k"] = top_k
        return []

    monkeypatch.setenv("FAKE_PROVIDER", "1")
    monkeypatch.setenv("MENTORCORE_PERSONAS_ROOT", str(root))
    import runtime.api.chat as chat_module
    from runtime.api.app import app

    monkeypatch.setattr(chat_module, "retrieve", fake_retrieve)
    client = TestClient(app)

    resp = client.post("/api/chat/test-persona", json={"message": "Hello"})

    assert resp.status_code == 200
    assert observed["top_k"] == 8


def test_health_endpoint(client):
    resp = client.get("/health")
    assert resp.status_code == 200
    assert resp.json()["status"] == "ok"
