"""Tests for the question-bank + custom-paper assembly router (/api/exam/bank, /custom-papers) — mode 3.

Same FakeSupabase + dependency-override pattern as test_exam_templates.py; the as-user RLS itself is
verified against the live dev DB (.94), not here.
"""
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient

import routers.exam.bank as bank_mod
from routers.exam.bank import router
from routers.exam.dependencies import ExamContext, get_exam_context


TEACHER = "00000000-0000-0000-0000-000000000001"
INST_A = "10000000-0000-0000-0000-000000000001"


@pytest.fixture(autouse=True)
def _stub_projection(monkeypatch):
    monkeypatch.setattr(bank_mod, "project_template_safe", lambda tid: None)


class FakeResult:
    def __init__(self, data):
        self.data = data


class FakeQuery:
    def __init__(self, store, table):
        self.store = store
        self.table = table
        self.rows = list(store.get(table, []))
        self._op = None
        self._payload = None

    def select(self, *_a, **_k):
        self._op = "select"
        return self

    def insert(self, payload):
        self._op = "insert"
        self._payload = payload
        return self

    def eq(self, key, value):
        self.rows = [r for r in self.rows if r.get(key) == value]
        return self

    def in_(self, key, values):
        values = set(values)
        self.rows = [r for r in self.rows if r.get(key) in values]
        return self

    def execute(self):
        backing = self.store.setdefault(self.table, [])
        if self._op == "insert":
            payloads = self._payload if isinstance(self._payload, list) else [self._payload]
            inserted = []
            for p in payloads:
                row = dict(p)
                row.setdefault("id", f"gen-{self.table}-{len(backing)}")
                backing.append(row)
                inserted.append(row)
            return FakeResult(inserted)
        return FakeResult(self.rows)


class FakeSupabase:
    def __init__(self, store):
        self.store = store

    def table(self, name):
        return FakeQuery(self.store, name)


def make_client(store, user_id=TEACHER, institute_ids=(INST_A,)):
    app = FastAPI()
    app.include_router(router, prefix="/api/exam")
    app.dependency_overrides[get_exam_context] = lambda: ExamContext(user_id, "tok", FakeSupabase(store), list(institute_ids))
    return TestClient(app), store


def test_bank_lists_leaf_questions_with_facets_excluding_containers():
    store = {"exam_questions": [
        {"id": "c1", "template_id": "t1", "label": "Q1", "is_container": True, "spec_ref": None,
         "exam_templates": {"id": "t1", "title": "Physics 1H", "subject": "physics", "exam_code": "AQA-PHYS-8463"}},
        {"id": "q1", "template_id": "t1", "label": "01.1", "is_container": False, "max_marks": 3, "spec_ref": "4.2",
         "exam_templates": {"id": "t1", "title": "Physics 1H", "subject": "physics", "exam_code": "AQA-PHYS-8463"}},
        {"id": "q2", "template_id": "t1", "label": "01.2", "is_container": False, "max_marks": 2, "spec_ref": "4.2",
         "exam_templates": {"id": "t1", "title": "Physics 1H", "subject": "physics", "exam_code": "AQA-PHYS-8463"}},
    ]}
    c, _ = make_client(store)
    body = c.get("/api/exam/bank").json()
    labels = {q["label"] for q in body["questions"]}
    assert labels == {"01.1", "01.2"}  # container excluded
    assert body["facets"]["spec_ref"] == {"4.2": 2}
    assert body["questions"][0]["paper"]["exam_code"] == "AQA-PHYS-8463"


def test_bank_filters_by_spec_ref():
    store = {"exam_questions": [
        {"id": "q1", "template_id": "t1", "label": "a", "is_container": False, "spec_ref": "4.2", "exam_templates": {"subject": "physics"}},
        {"id": "q2", "template_id": "t1", "label": "b", "is_container": False, "spec_ref": "4.5", "exam_templates": {"subject": "physics"}},
    ]}
    c, _ = make_client(store)
    body = c.get("/api/exam/bank?spec_ref=4.2").json()
    assert [q["id"] for q in body["questions"]] == ["q1"]


def test_custom_paper_copies_selected_questions_in_order():
    store = {"exam_questions": [
        {"id": "q1", "template_id": "t1", "label": "01.1", "is_container": False, "max_marks": 3, "spec_ref": "4.2", "answer_type": "short"},
        {"id": "q2", "template_id": "t2", "label": "05.1", "is_container": False, "max_marks": 6, "spec_ref": "4.5", "answer_type": "written"},
    ]}
    c, store = make_client(store)
    r = c.post("/api/exam/custom-papers", json={"title": "Electricity mini-test", "subject": "physics", "question_ids": ["q2", "q1"]})
    assert r.status_code == 200
    body = r.json()
    assert body["n_questions"] == 2
    # a new template row was created, owned + institute-scoped
    new_tpls = [t for t in store["exam_templates"] if t["id"] == body["id"]]
    assert new_tpls and new_tpls[0]["teacher_id"] == TEACHER and new_tpls[0]["institute_id"] == INST_A
    # two COPIES exist under the new template, order preserving the requested [q2, q1], with fresh ids
    copies = sorted([q for q in store["exam_questions"] if q["template_id"] == body["id"]], key=lambda q: q["order"])
    assert [q["label"] for q in copies] == ["05.1", "01.1"]
    assert all(q["id"] not in ("q1", "q2") for q in copies)  # not the originals
    assert copies[0]["max_marks"] == 6 and copies[0]["spec_ref"] == "4.5"


def test_custom_paper_requires_accessible_questions():
    c, _ = make_client({"exam_questions": []})
    assert c.post("/api/exam/custom-papers", json={"title": "x", "question_ids": ["nope"]}).status_code == 404
    assert c.post("/api/exam/custom-papers", json={"title": "x", "question_ids": []}).status_code == 400
