"""Chronology-safe 128-shell revalidation of the exact AP1-M1 junction.

The first 96-shell value existed in the shared workspace before the stricter
96-shell tolerance artifact was written.  Therefore 96 is conservatively
demoted to pilot data and cannot authorize a seed.  This module freezes the
unchanged strict caps against an unseen 128-shell level before evaluating it.
"""
from __future__ import annotations

from datetime import datetime, timezone
import json
from pathlib import Path
from typing import Any

import numpy as np

from ap1_m1_background_preflight import decode_hard_pass_state
from ap1_m1_junction_convergence import (
    complete_frozen_trajectory,
    evaluate_prefix_level,
    initial_probe_delta,
)
from ap1_m1_junction_heldout import METRIC_KEYS, delta_gates
from ap1_m1_junction_seed import file_sha256
from ap1_r2c_self_consistent_candidate import classical_terms
from chi_background_closure import ChiParameters, curvature_uv_tail_n


FIRST_96_RESULT_UTC = "2026-09-02T17:57:50Z"
FIRST_96_REMOVED_SHA256 = (
    "b3dfba658803c69335ebb85f4c7fb771d0b38cf4fc9ccb712ce6c2e1a4809c1b"
)
HELDOUT_MOMENTUM_NODES = 128
EXPECTED_STRICT_96_CLASSIFICATION = (
    "M1_EXACT_INITIAL_JUNCTION_96_SHELL_PASS_SEED_RELEASED_BACKGROUND_SOLVE_OPEN"
)


class RevalidationError(RuntimeError):
    """The chronology-safe revalidation prerequisites are not exact."""


def load_json(path: Path) -> dict[str, Any]:
    value = json.loads(path.read_text(encoding="utf-8"))
    if not isinstance(value, dict):
        raise RevalidationError(f"JSON root is not an object: {path}")
    return value


def build_128_freeze(
    pilot_path: Path,
    strict_96_tolerance_path: Path,
    strict_96_result_path: Path,
    convergence_code_path: Path,
    heldout_code_path: Path,
    revalidation_code_path: Path,
) -> dict[str, Any]:
    pilot = load_json(pilot_path)
    tolerance = load_json(strict_96_tolerance_path)
    result96 = load_json(strict_96_result_path)
    if result96.get("classification") != EXPECTED_STRICT_96_CLASSIFICATION:
        raise RevalidationError("strict 96-shell result is not numerically PASS")
    if not result96.get("all_exact_initial_junction_gates_pass"):
        raise RevalidationError("strict 96-shell numerical gates are incomplete")
    if not tolerance.get("all_tolerance_freeze_gates_pass"):
        raise RevalidationError("strict 96-shell tolerance artifact is incomplete")
    if tolerance["input_sha256"].get(pilot_path.name) != file_sha256(pilot_path):
        raise RevalidationError("pilot hash differs from the strict tolerance input")
    if result96["input_sha256"].get(strict_96_tolerance_path.name) != file_sha256(
        strict_96_tolerance_path
    ):
        raise RevalidationError("strict 96-shell result/tolerance hash mismatch")
    strict_freeze_utc = str(tolerance["updated_utc"])
    chronology_contaminated = FIRST_96_RESULT_UTC < strict_freeze_utc
    if not chronology_contaminated:
        raise RevalidationError("recorded chronology does not require revalidation")

    thresholds96 = result96["frozen_thresholds"]
    caps = {
        key: float(thresholds96["heldout_delta_caps"][key])
        for key in METRIC_KEYS
    }
    if not all(np.isfinite(value) and value > 0.0 for value in caps.values()):
        raise RevalidationError("strict momentum caps are not positive and finite")
    gates = {
        "96_numerical_result_pass_but_seed_eligibility_revoked": True,
        "first_96_precedes_strict_96_freeze": chronology_contaminated,
        "first_96_unregistered_artifact_removed_before_seed_use": True,
        "strict_caps_carried_forward_unchanged": caps
        == thresholds96["heldout_delta_caps"],
        "128_shell_value_not_evaluated": True,
        "pilot_and_strict_artifacts_hash_bound": True,
        "background_and_kernel_not_started": (
            not bool(result96.get("new_AP1_M1_background_started"))
            and not bool(result96.get("physical_response_kernel_started"))
        ),
    }
    passed = bool(all(gates.values()))
    return {
        "schema": "apeiron-ap1-m1-exact-initial-junction-128-freeze-v1.0",
        "updated_utc": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
        "classification": (
            "M1_EXACT_INITIAL_JUNCTION_128_TOLERANCES_FROZEN_HELDOUT_UNSEEN"
            if passed
            else "M1_EXACT_INITIAL_JUNCTION_128_FREEZE_NOT_PASS"
        ),
        "input_sha256": {
            pilot_path.name: file_sha256(pilot_path),
            strict_96_tolerance_path.name: file_sha256(strict_96_tolerance_path),
            strict_96_result_path.name: file_sha256(strict_96_result_path),
            "AP1/CODE/ap1_m1_junction_convergence.py": file_sha256(
                convergence_code_path
            ),
            "AP1/CODE/ap1_m1_junction_heldout.py": file_sha256(
                heldout_code_path
            ),
            "AP1/CODE/ap1_m1_junction_128_revalidation.py": file_sha256(
                revalidation_code_path
            ),
        },
        "chronology_audit": {
            "first_96_result_utc": FIRST_96_RESULT_UTC,
            "first_96_removed_unregistered_sha256": FIRST_96_REMOVED_SHA256,
            "strict_96_freeze_utc": strict_freeze_utc,
            "strict_96_result_utc": result96["updated_utc"],
            "96_can_remain_numerical_pilot": True,
            "96_seed_eligibility_revoked": True,
            "96_seed_was_used_for_background": False,
            "reason": "the first 96-shell value existed before the stricter 96-shell caps were written in the shared workspace",
        },
        "junction_rule": result96["junction_rule"],
        "reference_96_shell_probe": result96["heldout_level"][
            "exact_initial_HARD_PASS_junction_probe"
        ],
        "thresholds": {
            "heldout_momentum_nodes": HELDOUT_MOMENTUM_NODES,
            "evaluation_stride": int(thresholds96["evaluation_stride"]),
            "prefix_intervals": int(thresholds96["prefix_intervals"]),
            "decimal_digits": int(thresholds96["decimal_digits"]),
            "heldout_delta_caps": caps,
            "max_abs_raychaudhuri_normalized": float(
                thresholds96["max_abs_raychaudhuri_normalized"]
            ),
            "max_abs_friedmann_normalized": float(
                thresholds96["max_abs_friedmann_normalized"]
            ),
            "max_wronskian_relative_error": float(
                thresholds96["max_wronskian_relative_error"]
            ),
            "require_exact_stored_index": 0,
            "require_positive_standard_density": True,
            "require_positive_inherited_frequency_margin": True,
        },
        "cap_policy": "carry the already strict 96-shell caps forward unchanged; evaluate 128 exactly once after this hash-bound freeze",
        "gates": gates,
        "all_128_freeze_gates_pass": passed,
        "tolerances_frozen_before_128_shell_heldout": passed,
        "heldout_128_evaluated": False,
        "checkpoint_released": False,
        "seed_released": False,
        "new_AP1_M1_background_started": False,
        "physical_response_kernel_started": False,
        "nonpass_stored_or_used_as_seed": False,
        "next_required": "evaluate the unseen 128-shell exact-index-zero probe once; release no seed unless every frozen gate passes",
        "claim_boundary": "chronology repair and 128-shell tolerance freeze only; no accepted M1 seed, background, kernel or observable",
    }


def build_128_heldout(
    apeiron_root: Path,
    freeze_path: Path,
    strict_96_result_path: Path,
    pilot_path: Path,
    strict_96_tolerance_path: Path,
    convergence_code_path: Path,
    heldout_code_path: Path,
    revalidation_code_path: Path,
) -> dict[str, Any]:
    freeze = load_json(freeze_path)
    result96 = load_json(strict_96_result_path)
    if not freeze.get("all_128_freeze_gates_pass") or not freeze.get(
        "tolerances_frozen_before_128_shell_heldout"
    ):
        raise RevalidationError("128-shell freeze is not PASS")
    if freeze.get("heldout_128_evaluated"):
        raise RevalidationError("128-shell level was already marked evaluated")
    expected_hashes = {
        pilot_path.name: file_sha256(pilot_path),
        strict_96_tolerance_path.name: file_sha256(strict_96_tolerance_path),
        strict_96_result_path.name: file_sha256(strict_96_result_path),
        "AP1/CODE/ap1_m1_junction_convergence.py": file_sha256(
            convergence_code_path
        ),
        "AP1/CODE/ap1_m1_junction_heldout.py": file_sha256(heldout_code_path),
        "AP1/CODE/ap1_m1_junction_128_revalidation.py": file_sha256(
            revalidation_code_path
        ),
    }
    if freeze.get("input_sha256") != expected_hashes:
        raise RevalidationError("128-shell freeze authority changed")

    thresholds = freeze["thresholds"]
    frozen = decode_hard_pass_state(
        apeiron_root / "HANDOFF/CURRENT_STATE_LATEST.npz"
    )
    trajectory = complete_frozen_trajectory(frozen)
    full_tail = curvature_uv_tail_n(
        trajectory,
        ChiParameters(),
        nodes_per_octave=6,
    )
    level128 = evaluate_prefix_level(
        frozen,
        int(thresholds["prefix_intervals"]),
        int(thresholds["evaluation_stride"]),
        int(thresholds["heldout_momentum_nodes"]),
        int(thresholds["decimal_digits"]),
        full_tail,
    )
    baseline96 = result96["heldout_level"]
    delta = initial_probe_delta(baseline96, level128)
    probe = level128["exact_initial_HARD_PASS_junction_probe"]
    kinetic = classical_terms(frozen[0, :5])
    gates = {
        **delta_gates(delta, thresholds["heldout_delta_caps"]),
        "128_level_finite": bool(level128["finite"]),
        "exact_stored_index_zero_used": (
            int(probe["stored_index"])
            == int(thresholds["require_exact_stored_index"])
            and float(probe["N_local"]) == 0.0
        ),
        "friedmann_below_frozen_cap": abs(float(probe["friedmann_normalized"]))
        <= float(thresholds["max_abs_friedmann_normalized"]),
        "raychaudhuri_below_frozen_cap": abs(
            float(probe["raychaudhuri_normalized"])
        )
        <= float(thresholds["max_abs_raychaudhuri_normalized"]),
        "positive_standard_density": float(
            probe["required_standard_density_Mpl4"]
        )
        > 0.0,
        "positive_inherited_frequency_margin": bool(
            probe["positive_vacuum_frequency_margin"]
        )
        and float(probe["vacuum_frequency_margin_Mpl2"]) > 0.0,
        "positive_H_PX_K": (
            float(frozen[0, 4]) > 0.0
            and float(kinetic["PX"]) > 0.0
            and float(kinetic["K"]) > 0.0
        ),
        "wronskian_below_frozen_cap": float(level128["wronskian_relative_error"])
        <= float(thresholds["max_wronskian_relative_error"]),
        "freeze_hash_bound_before_128_evaluation": True,
        "old_solver_or_physical_map_not_called": True,
        "no_interpolated_root_used": True,
    }
    passed = bool(all(gates.values()))
    return {
        "schema": "apeiron-ap1-m1-exact-initial-junction-128-heldout-v1.0",
        "updated_utc": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
        "classification": (
            "M1_EXACT_INITIAL_JUNCTION_128_SHELL_PASS_SEED_CONSTRUCTION_OPEN"
            if passed
            else "M1_EXACT_INITIAL_JUNCTION_128_SHELL_NOT_PASS_NO_SEED"
        ),
        "input_sha256": {
            freeze_path.name: file_sha256(freeze_path),
            **expected_hashes,
            "HANDOFF/CURRENT_STATE_LATEST.npz": file_sha256(
                apeiron_root / "HANDOFF/CURRENT_STATE_LATEST.npz"
            ),
        },
        "heldout_was_unseen_when_128_tolerances_frozen": True,
        "prior_96_role": "pilot_only_seed_eligibility_revoked_by_chronology_audit",
        "junction_rule": freeze["junction_rule"],
        "frozen_thresholds": thresholds,
        "reference_96_shell_probe": freeze["reference_96_shell_probe"],
        "heldout_128_level": level128,
        "heldout_96_to_128_delta": delta,
        "gates": gates,
        "all_128_heldout_gates_pass": passed,
        "checkpoint_eligible": passed,
        "seed_construction_open": passed,
        "seed_released": False,
        "new_AP1_M1_background_started": False,
        "physical_response_kernel_started": False,
        "nonpass_stored_or_used_as_seed": False,
        "equations_changed": False,
        "physics_changed": False,
        "parameters_changed": False,
        "existing_gate_thresholds_changed": False,
        "next_required": (
            "materialize and validate the exact 128-shell junction seed, then open the bounded N-background pilot"
            if passed
            else "stop fail-closed; do not construct or use an M1 seed"
        ),
        "claim_boundary": "chronology-safe 128-shell junction validation only; no M1 background, kernel, observable, fit or significance",
    }


def main() -> None:
    import argparse

    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest="command", required=True)
    freeze_parser = subparsers.add_parser("freeze")
    freeze_parser.add_argument("pilot", type=Path)
    freeze_parser.add_argument("strict_96_tolerance", type=Path)
    freeze_parser.add_argument("strict_96_result", type=Path)
    freeze_parser.add_argument("convergence_code", type=Path)
    freeze_parser.add_argument("heldout_code", type=Path)
    freeze_parser.add_argument("revalidation_code", type=Path)
    freeze_parser.add_argument("--output", type=Path, required=True)
    heldout_parser = subparsers.add_parser("heldout")
    heldout_parser.add_argument("apeiron_root", type=Path)
    heldout_parser.add_argument("freeze", type=Path)
    heldout_parser.add_argument("strict_96_result", type=Path)
    heldout_parser.add_argument("pilot", type=Path)
    heldout_parser.add_argument("strict_96_tolerance", type=Path)
    heldout_parser.add_argument("convergence_code", type=Path)
    heldout_parser.add_argument("heldout_code", type=Path)
    heldout_parser.add_argument("revalidation_code", type=Path)
    heldout_parser.add_argument("--output", type=Path, required=True)
    args = parser.parse_args()

    if args.command == "freeze":
        report = build_128_freeze(
            args.pilot,
            args.strict_96_tolerance,
            args.strict_96_result,
            args.convergence_code,
            args.heldout_code,
            args.revalidation_code,
        )
    else:
        report = build_128_heldout(
            args.apeiron_root,
            args.freeze,
            args.strict_96_result,
            args.pilot,
            args.strict_96_tolerance,
            args.convergence_code,
            args.heldout_code,
            args.revalidation_code,
        )
    args.output.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")


if __name__ == "__main__":
    main()
