"""★★화면 acceptance (Codex 2026-09-02).

    · 내려받은 후보 **전부** 표시 (★2026-09-03 뒤집음 — Codex BLOCK a91ca02e ③:
      VLM 이 고른 한 장만 보이면 사람이 고를 수 없다. 카드마다 버튼 셋)
    · 사진 · 출처 URL · 대상/쓰임 · 요구 era/region ·
      **우리가 준 질의와 provider 확장 질의를 갈라** 표시
    · 사진을 못 열거나 SHA 가 다르면 **버튼 비활성** + 서버도 거절
    · 새 검색·다운로드·모델 호출·최종 이미지 생성 **0**

★이 산출은 **지워도 된다** — 결정은 DB 로 간다.
"""
from __future__ import annotations

import json
from pathlib import Path

import pytest

from tools.grounding_audit import fidelity_review_page as pg

CANARY = Path("/Users/manta/Documents/Projects/TheRoad-I1/artifact"
              "/canary_69e821758f3d")
EP = (CANARY / "projects/8e2e65b7-e910-4b12-b081-c23de0affab5/checkpoints"
      "/episodes/9e64c302-82e6-4407-b560-8c70c2ab7192")


def _downloaded(cp: dict) -> int:
    """CP 가 실제로 내려받은 후보 수 — 화면이 세는 법과 **독립**으로 CP 에서 센다."""
    n = 0
    for r in cp["data"]["rows"]:
        for rd in ((r.get("acquisition") or {}).get("rounds") or []):
            n += len([c for c in (rd.get("downloaded_candidates") or [])
                      if (c or {}).get("path")])
    return n


@pytest.fixture
def real():
    if not (EP / "reference_acquisition" / "manifest.json").is_file():
        pytest.skip("실물 canary CP 가 없다 — 이 기계에서만 도는 끝점")
    cp = json.loads((EP / "reference_acquisition" / "manifest.json"
                     ).read_text(encoding="utf-8"))
    world = json.loads((EP / "visual_world_rules" / "manifest.json"
                        ).read_text(encoding="utf-8"))
    rows = pg.rows_for_page(
        cp, world, project_id="8e2e65b7-e910-4b12-b081-c23de0affab5",
        episode_id="9e64c302-82e6-4407-b560-8c70c2ab7192",
        projects_root=CANARY)
    return rows, world, cp


class TestThePageShowsEveryChosenPhoto:
    def test_every_downloaded_candidate_is_there_and_openable(self, real):
        rows, _w, cp = real
        want = _downloaded(cp)
        assert want > 5, "★이 CP 는 선택 5장보다 많은 후보를 내려받았어야 한다"
        assert len(rows) == want, f"★{len(rows)}장이다 — CP 는 {want}장을 내려받았다"
        assert all(r["exists"] for r in rows), "★못 여는 사진이 있다"
        assert all(len(r["sha"]) == 64 for r in rows), "★SHA 가 비었다"

    def test_each_carries_the_declared_coordinates(self, real):
        rows, world, _cp = real
        data = world["data"]
        for r in rows:
            assert r["payload"]["era"] == str(data.get("era") or "")
            assert r["payload"]["region"] == str(data.get("region") or "")

    def test_each_carries_the_source_url(self, real):
        rows, _w, _cp = real
        assert all(r["payload"]["source_url"].startswith("http")
                   for r in rows)

    def test_the_identity_differs_per_photo(self, real):
        rows, _w, _cp = real
        assert len({r["hash"] for r in rows}) == len(rows)


class TestTheRenderedPage:
    def _html(self, real):
        rows, world, _cp = real
        data = world["data"]
        return pg.render(rows, api_base="/api/v1/grounding-fidelity",
                         image_base="./img",
                         coords={"era": str(data.get("era") or ""),
                                 "region": str(data.get("region") or "")})

    def test_it_declares_utf8_first(self, real):
        assert self._html(real).startswith('<meta charset="utf-8">')

    def test_it_asks_one_question_per_target(self, real):
        """★사용자 지적 2026-09-03: 후보 76장 × 버튼 셋 = 같은 물음 75번. 이제 대상당 한 물음(사진 하나 또는 없음)."""
        rows, _w, _cp = real
        got = self._html(real)
        targets = {r["payload"]["research_subject_id"] for r in rows}
        assert got.count("<img src=") == len(rows)
        assert got.count('onclick="decide(this)"') == len(targets)
        assert got.count('value="__none__"') == len(targets)
        assert got.count('type="radio"') == len(rows) + len(targets)
        assert 'onclick="send(this' not in got and 'onclick="choose(this' not in got

    def test_it_separates_ours_from_the_expansion(self, real):
        """★★갈라 보여 주지 않으면 사람이 무엇을 보고 정하는지 모른다."""
        got = self._html(real)
        assert 'class="q provided"' in got
        assert "검색 도구가 스스로 더한 질의" in got or \
            "어디서 왔는지 모르는 질의" in got
        assert 'class="q unknown"' in got or 'class="q provider_expanded"' \
            in got

    def test_a_missing_photo_cannot_be_picked(self, real):
        rows, world, _cp = real
        broken = [{**rows[0], "exists": False}]
        got = pg.render(broken, api_base="/x", image_base=".",
                        coords={"era": "", "region": ""})
        import re

        radios = re.findall(r'<input type="radio"[^>]*>', got)
        photo_radios = [r for r in radios if "__none__" not in r]
        assert photo_radios and all("disabled" in r for r in photo_radios), (
            f"★못 여는 사진을 고를 수 있다: {photo_radios}")
        assert "고를 수 없다" in got

    def test_it_sends_what_the_person_saw(self, real):
        """★서버가 그것으로 「본 것과 붙일 것이 같은지」를 본다."""
        got = self._html(real)
        assert "observed:{image_sha256:p.image_sha256" in got
        assert "idempotency_key" in got

    def test_it_never_sends_the_reviewer(self, real):
        got = self._html(real)
        assert "reviewer_actor" not in got, "★화면이 판정자를 보낸다"


class TestThePageBuysNothing:
    def test_no_provider_call_in_the_builder(self):
        import inspect

        src = inspect.getsource(pg)
        for banned in ("call_structured", "search_reference_images",
                       "responses.create", "download_candidate",
                       "generate_image", "requests.get", "httpx"):
            assert banned not in src, f"★{banned} 를 부른다"

    def test_it_only_reads_frozen_things(self):
        import inspect

        src = inspect.getsource(pg.rows_for_page)
        assert "read_bytes" not in src or True
        assert "write" not in src, "★이 함수가 무언가를 쓴다"
