"""Session engine pins — status, progress, checklist, and export payload.

Progress and status are this study's "numbers"; these are the hand-checked
acceptance values the in-notebook gate re-pins (Pattern §4). Every engine
function takes the bank as its first argument; the ``topics`` fixture
supplies the real bank from the notebook's topic-bank cell.
"""

from discoverylib import (
    COMPLETE,
    IN_PROGRESS,
    NOT_STARTED,
    SKIPPED,
    active_topic_key,
    build_session,
    normalize_status,
    progress,
    session_json,
    subtopic_checklist,
    subtopic_id,
)


def test_normalize_status_defaults_unknown():
    assert normalize_status("complete") == COMPLETE
    assert normalize_status(None) == NOT_STARTED
    assert normalize_status("garbage") == NOT_STARTED


def test_progress_all_not_started(topics):
    p = progress(topics, {})
    assert p.total == 8
    assert (p.completed, p.skipped, p.in_progress, p.not_started) == (0, 0, 0, 8)
    assert p.fraction == 0.0
    assert p.resolved == 0
    assert p.label == "0/8 topics complete"


def test_progress_mixed_counts_and_fraction(topics):
    status = {
        "background": COMPLETE,
        "cx_strategy": COMPLETE,
        "channels": IN_PROGRESS,
        "agent_environment": SKIPPED,
        # remaining four default to not_started
    }
    p = progress(topics, status)
    assert (p.completed, p.skipped, p.in_progress, p.not_started) == (2, 1, 1, 4)
    assert p.resolved == 3                       # complete + skipped
    assert p.fraction == 2 / 8
    assert p.label == "2/8 topics complete"


def test_active_topic_prefers_in_progress_then_first_open(topics):
    # in-progress wins even if a later topic is also in progress
    assert active_topic_key(topics, {"channels": IN_PROGRESS}) == "channels"
    # no in-progress → first not-started in canonical order
    assert active_topic_key(topics, {"background": COMPLETE}) == "cx_strategy"
    # everything resolved → None
    all_done = {t.key: COMPLETE for t in topics}
    assert active_topic_key(topics, all_done) is None


def test_subtopic_checklist_marks_done(topic_by_key):
    t = topic_by_key["channels"]
    done = {subtopic_id("channels", "voice_metrics")}
    items = subtopic_checklist(t, done)
    assert len(items) == len(t.subtopics)
    by_key = {i.key: i.done for i in items}
    assert by_key["voice_metrics"] is True
    assert by_key["inbound_context"] is False


def test_build_session_shape_and_subtopic_done_count(topics, topic_by_key):
    status = {"channels": IN_PROGRESS, "background": COMPLETE}
    notes = {"channels": "  6 channels; voice ~70%  "}
    done = {subtopic_id("channels", "voice_metrics"),
            subtopic_id("channels", "outbound")}
    s = build_session(topics, status, notes, done)

    assert len(s["topics"]) == 8
    assert s["active_topic_key"] == "channels"
    assert s["active_topic"].title == "Channels"
    assert s["agenda_minutes"] == 110

    ch = next(ts for ts in s["topics"] if ts.key == "channels")
    assert ch.status == IN_PROGRESS
    assert ch.subtopics_done == 2
    assert ch.subtopics_total == len(topic_by_key["channels"].subtopics)
    # WHICH threads were covered, in canonical bank order — the export's
    # whole purpose is naming them, not just counting them.
    assert ch.subtopics_covered == ("voice_metrics", "outbound")
    assert ch.notes == "6 channels; voice ~70%"        # trimmed

    bg = next(ts for ts in s["topics"] if ts.key == "background")
    assert bg.status == COMPLETE and bg.subtopics_done == 0
    assert bg.subtopics_covered == ()


def test_session_json_is_plain_and_complete(topics):
    status = {"background": COMPLETE, "channels": IN_PROGRESS}
    notes = {"background": "3 LOBs; PCI in scope"}
    done = {subtopic_id("channels", "voice_metrics")}
    payload = session_json(build_session(topics, status, notes, done),
                           meta={"client": "Acme", "date": "2026-07-19"})

    assert payload["assessment"] == "CX_Discovery_Workshop"
    assert payload["meta"]["client"] == "Acme"
    assert payload["progress"]["completed"] == 1
    assert payload["progress"]["fraction_complete"] == round(1 / 8, 4)
    assert payload["active_topic"] == "channels"
    assert len(payload["topics"]) == 8
    bg = next(t for t in payload["topics"] if t["key"] == "background")
    assert bg["status"] == COMPLETE
    assert bg["notes"] == "3 LOBs; PCI in scope"
    ch = next(t for t in payload["topics"] if t["key"] == "channels")
    assert ch["subtopics_done"] == 1
    assert ch["subtopics_covered"] == ["voice_metrics"]

    # JSON-serializable (no dataclasses / sets leaked through)
    import json
    json.dumps(payload)
