#!/usr/bin/env python3
"""Capture source hashes and isolated compiled-expression results; needs network and cc."""
import csv
import hashlib
import io
import json
import platform
import subprocess
import tempfile
import urllib.request
from datetime import datetime, timezone
from pathlib import Path
from serial_order import compare, results

HERE = Path(__file__).resolve().parent
SOURCES = [
    ("linux-jbd2", "https://raw.githubusercontent.com/torvalds/linux/v6.16/include/linux/jbd2.h",
     ["int difference = (x - y);", "return (difference > 0);"]),
    ("postgres-transam", "https://raw.githubusercontent.com/postgres/postgres/REL_18_0/src/backend/access/transam/transam.c",
     ["diff = (int32) (id1 - id2);", "return (diff < 0);"]),
    ("postgres-header", "https://raw.githubusercontent.com/postgres/postgres/REL_18_0/src/include/access/transam.h",
     ["#define FirstNormalTransactionId ((TransactionId) 3)", "result.value = ((uint64) epoch) << 32 | xid;"]),
]


def run():
    sources = []
    for source_id, url, markers in SOURCES:
        with urllib.request.urlopen(url, timeout=30) as response:
            raw = response.read()
        flattened = " ".join(raw.decode().split())
        if not all(marker in flattened for marker in markers):
            raise RuntimeError("source marker changed: " + source_id)
        sources.append({"id": source_id, "url": url, "sha256": hashlib.sha256(raw).hexdigest(),
                        "bytes": len(raw), "markersVerified": True})
    cases = results()["boundaryCases"]
    with tempfile.TemporaryDirectory(prefix="hesela-serial-") as tmp:
        binary = Path(tmp) / "probe"
        subprocess.run(["cc", "-std=c11", "-Wall", "-Wextra", "-Werror", "-O2",
                        str(HERE / "serial_order_probe.c"), "-o", str(binary)], check=True)
        input_text = "".join(f'{row["a"]} {row["b"]}\n' for row in cases)
        output = subprocess.run([str(binary)], input=input_text, text=True,
                                capture_output=True, check=True).stdout
    rows = [{key: int(value) for key, value in row.items()}
            for row in csv.DictReader(io.StringIO(output))]
    if len(rows) != len(cases):
        raise RuntimeError("native probe omitted rows")
    for case, row in zip(cases, rows):
        if (row["a"], row["b"]) != (case["a"], case["b"]):
            raise RuntimeError("native inputs changed")
        relation = compare(row["a"], row["b"])
        expected = (1, 1, 0, 0) if relation == "ambiguous" else (
            int(relation == "older"), int(relation == "newer"),
            int(relation == "older"), int(relation == "newer"))
        actual = tuple(row[k] for k in ("pg_normal_before_ab", "pg_normal_before_ba",
                                       "jbd2_gt_ba", "jbd2_gt_ab"))
        if actual != expected:
            raise RuntimeError(f"unexpected compiler result: {row}")
        row["case"] = case["case"]
        row["guardedRelation"] = relation
    return {
        "generatedAt": datetime.now(timezone.utc).isoformat(),
        "evidenceType": "source-marker checks and isolated C expression execution",
        "environment": {"python": platform.python_version(), "platform": platform.platform(),
                        "compiler": subprocess.check_output(["cc", "--version"], text=True).splitlines()[0]},
        "sources": sources,
        "harnessSha256": {p.name: hashlib.sha256(p.read_bytes()).hexdigest()
                          for p in [HERE / "serial_order.py", HERE / "serial_order_probe.c",
                                    HERE / "capture_serial_order.py", HERE / "test_serial_order.py"]},
        "cases": rows,
        "limits": ["Not linked against either project; only the documented normal-XID arithmetic branch is probed.",
                   "Unsigned-to-signed conversion is implementation-defined in C11; results describe this compiler.",
                   "No claim that real journal or database state reaches half-range inputs.",
                   "RFC 1982 supplies a comparison model, not a storage-format specification."]
    }


if __name__ == "__main__":
    print(json.dumps(run(), indent=2, sort_keys=True))
