"""Validate recorded Choice answers and preview fixed actions, without execution."""
import hashlib
import json
import math
import re
import sys
from pathlib import Path


def preview_workflow(fixture, observations):
    if fixture.get("kind") != "original-bounded-choice-workflow" or fixture.get("version") != 1:
        raise ValueError("Unsupported fixture version")
    question, choices = fixture.get("question"), fixture.get("choices")
    if not isinstance(question, str) or not 1 <= len(question) <= 500:
        raise ValueError("Expected an explicit bounded rubric")
    if not isinstance(choices, list) or not 2 <= len(choices) <= 12 or any(not isinstance(s, str) or not 1 <= len(s) <= 120 for s in choices) or len(set(choices)) != len(choices):
        raise ValueError("Invalid choice labels")
    actions = fixture.get("actions")
    if not isinstance(actions, dict) or set(actions) != set(choices):
        raise ValueError("Every label needs an explicit preview mapping")
    for action in actions.values():
        if not isinstance(action, dict) or set(action) != {"action", "payload", "pending"}:
            raise ValueError("Invalid preview mapping")
        if not isinstance(action["action"], str) or not re.fullmatch(r"[a-z][a-z0-9_]{0,79}", action["action"]) or type(action["pending"]) is not bool:
            raise ValueError("Invalid action name or pending flag")
        if len(json.dumps(action["payload"], allow_nan=False).encode()) > 8000:
            raise ValueError("Preview payload is too large")
    samples, results = fixture.get("samples"), observations.get("results")
    if not isinstance(samples, list) or not 1 <= len(samples) <= 1000 or not isinstance(results, list) or len(results) != len(samples):
        raise ValueError("Each sample needs exactly one observation")
    by_result = {}
    for result in results:
        identifier = result.get("id")
        if not isinstance(identifier, str) or identifier in by_result:
            raise ValueError("Invalid or duplicate observation ID")
        by_result[identifier] = result
    seen, output = set(), []
    for sample in samples:
        identifier, request = sample.get("id"), sample.get("request")
        if not isinstance(identifier, str) or not re.fullmatch(r"[a-z0-9-]{1,80}", identifier) or identifier in seen:
            raise ValueError("Invalid or duplicate sample ID")
        seen.add(identifier)
        if not isinstance(request, dict) or set(request) != {"context", "question", "choices"} or not isinstance(request["context"], str) or not 1 <= len(request["context"]) <= 12000:
            raise ValueError("Invalid request")
        if request["question"] != question or request["choices"] != choices:
            raise ValueError("Request does not match the fixture rubric")
        result = by_result.get(identifier)
        if result is None:
            raise ValueError("Missing observation")
        canonical = {"context": request["context"], "question": request["question"], "choices": request["choices"]}
        digest = hashlib.sha256(json.dumps(canonical, separators=(",", ":"), ensure_ascii=False).encode()).hexdigest()
        if result.get("requestSha256") != digest:
            raise ValueError("Stale result: request identity changed")
        probabilities = result.get("probabilities", {})
        if set(probabilities) != set(choices):
            raise ValueError("Unexpected probability labels")
        values = list(probabilities.values())
        if any(type(v) not in (int, float) or not math.isfinite(v) or not 0 <= v <= 1 for v in values) or not math.isclose(sum(values), 1, abs_tol=0.02):
            raise ValueError("Invalid probability distribution")
        leaders = [label for label in choices if probabilities[label] == max(values)]
        if result.get("choice") not in leaders:
            raise ValueError("Recorded label is not a leading choice")
        mapping = actions[leaders[0]] if len(leaders) == 1 else {"action": "manual_tie_review", "payload": None, "pending": True}
        output.append({"sampleId": identifier, "leaders": leaders, "probabilities": probabilities, **mapping})
    return {"mode": "preview-only", "rows": output}


if __name__ == "__main__":
    if len(sys.argv) != 3:
        raise SystemExit("Usage: python preview_choice_workflow.py samples.json saved-api-results.json")
    inputs = []
    for path in sys.argv[1:]:
        raw = Path(path).read_bytes()
        if len(raw) > 1_000_000:
            raise ValueError("Input file is too large")
        inputs.append(json.loads(raw.decode("utf-8")))
    print(json.dumps(preview_workflow(*inputs), indent=2, allow_nan=False))
