#!/usr/bin/env python3
"""CC0. Re-run the published controlled cases over HTTP or modern MCP. No retries."""
import argparse
import hashlib
import json
from pathlib import Path
import urllib.request


def request(base, path, body=None, headers=None):
    data = None if body is None else json.dumps(body, ensure_ascii=False).encode()
    h = {"Accept": "application/json, text/event-stream", "User-Agent": "BOZOS-Source-Evaluation/1.0"}
    if data is not None:
        if len(data) > 65536:
            raise ValueError("Request exceeds 64 KiB")
        h["Content-Type"] = "application/json"
    h.update(headers or {})
    with urllib.request.urlopen(urllib.request.Request(base + path, data=data, headers=h), timeout=60) as response:
        return json.load(response)


def verify(manifest, results, fixture_base):
    expected = {case["id"]: case for case in manifest["cases"]}
    assert len(results) == len(expected)
    assert [r["id"] for r in results] == list(expected)
    for row in results:
        case = expected[row["id"]]
        if "error" in case:
            assert row["status"] == "failed" and row["error"] == case["error"], row["id"]
            assert row["quote_check"] is None, row["id"]
            if "http_status" in case:
                assert row["http_status"] == case["http_status"], row["id"]
            continue
        quote = row["quote_check"]
        assert row["status"] == "readable", row["id"]
        assert quote["status"] == case["status"] and quote["matched"] is case["matched"], row["id"]
        assert quote["source_truncated"] is case.get("truncated", False), row["id"]
        assert row["final_url"] == fixture_base + case.get("final_path", case["path"]), row["id"]
        assert len(row["redirects"]) == case.get("redirects", 0), row["id"]
        canonical_fixture = case.get("final_path", case["path"])
        known = next(f for f in manifest["fixtures"] if f["path"] == canonical_fixture)
        assert row["source_sha256"] == known["sha256"], row["id"]
        for match in quote["matches"]:
            if "page" in case:
                assert match["page_start"] == match["page_end"] == case["page"], row["id"]
            if "passage" in case:
                assert match["text"] == case["passage"], row["id"]
            assert match["end"] - match["start"] == len(match["text"]), row["id"]
    return len(results)


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--base-url", default="https://bozos.si")
    parser.add_argument("--transport", choices=["http", "mcp"], default="http")
    parser.add_argument("--output", required=True, help="New JSON output file")
    args = parser.parse_args()
    output = Path(args.output)
    if output.exists():
        raise ValueError("Output already exists; keep prior captures")
    base = args.base_url.rstrip("/")
    fixture_base = base + "/examples/source-reliability/fixtures"
    manifest = request(base, "/examples/source-reliability/corpus.json")
    results, replies = [], []
    for start in range(0, len(manifest["cases"]), 10):
        payload = {"citations": [{"id": c["id"], "url": fixture_base+c["path"], "quote": c["quote"]} for c in manifest["cases"][start:start+10]]}
        if args.transport == "http":
            reply = request(base, "/api/v1/citation-check", payload)
            evidence = reply
        else:
            revision = "2026-07-28"
            reply = request(base, "/api/mcp", {"jsonrpc": "2.0", "id": start+1, "method": "tools/call", "params": {"name": "checkCitations", "arguments": payload, "_meta": {"io.modelcontextprotocol/protocolVersion": revision, "io.modelcontextprotocol/clientCapabilities": {}}}}, {"MCP-Protocol-Version": revision, "Mcp-Method": "tools/call", "Mcp-Name": "checkCitations"})
            assert "error" not in reply and not reply["result"].get("isError"), reply
            assert reply["result"]["resultType"] == "complete"
            evidence = reply["result"]["structuredContent"]
        results.extend(evidence["results"])
        replies.append(reply)
    failure = None
    try:
        count = verify(manifest, results, fixture_base)
    except AssertionError as error:
        count = None
        failure = str(error)
    output.parent.mkdir(parents=True, exist_ok=True)
    with output.open("x") as target:
        json.dump({"transport": args.transport, "base_url": base, "passed": count, "verification_error": failure, "requests": 1+len(replies), "manifest_sha256": hashlib.sha256(json.dumps(manifest, sort_keys=True).encode()).hexdigest(), "replies": replies}, target, ensure_ascii=False, indent=2)
        target.write("\n")
    if failure is not None:
        raise AssertionError(f"Verification failed: {failure}; raw replies saved to {output}")
    print(f"{count} controlled cases passed over {args.transport}; saved {output}")


if __name__ == "__main__":
    main()
