"""AP1-R2c audit of chi-mode inheritance at the frozen PASS junction.

The audit is read-only with respect to v7.13.  It does not evaluate terminal
residuals or rerun a background solver.  It asks one new AP1 question only:
can the chi state be reinitialized at the junction, or must its mode history
be inherited continuously into the new M1 trajectory?
"""
from __future__ import annotations

import json
from pathlib import Path

import numpy as np
from scipy.interpolate import CubicSpline

from ap1_m1_background_preflight import decode_hard_pass_state, file_sha256
from chi_background_closure import (
    ChiParameters,
    ChiTrajectory,
    TrajectoryError,
    physical_shells,
    portal_terms,
    propagate_modes_n,
)


def inherited_trajectory(state_path: Path, nodes: int) -> ChiTrajectory:
    frozen = decode_hard_pass_state(state_path)
    if not (17 <= nodes <= len(frozen)):
        raise ValueError("inheritance grid outside stored PASS trajectory")
    indices = np.unique(np.round(np.linspace(0, len(frozen) - 1, nodes)).astype(int))
    y = frozen[indices]
    N = y[:, 5]
    H = y[:, 4]
    Hdot = H * CubicSpline(N, H)(N, 1)
    return ChiTrajectory(N=N, H=H, Hdot=Hdot, sigma=y[:, 0], theta=y[:, 2]).validated()


def endpoint_reinitialization_rejected(state_path: Path) -> bool:
    frozen = decode_hard_pass_state(state_path)
    end = frozen[-1]
    N = np.linspace(0.0, 1.0e-4, 17)
    tr = ChiTrajectory(
        N=N,
        H=np.full_like(N, end[4]),
        Hdot=np.zeros_like(N),
        sigma=np.full_like(N, end[0]),
        theta=np.full_like(N, end[2]),
    )
    try:
        propagate_modes_n(tr, np.array([0.0]), ChiParameters().Lambda)
    except TrajectoryError as exc:
        return "non-positive initial canonical frequency" in str(exc)
    return False


def build_inheritance_report(state_path: Path, levels: tuple[int, ...] = (1025, 2049, 4097)) -> dict:
    p = ChiParameters()
    frozen = decode_hard_pass_state(state_path)
    mass2 = np.asarray(portal_terms(frozen[:, 0], frozen[:, 2], p)[0])
    first_nonpositive = np.flatnonzero(mass2 - 2.25 * frozen[:, 4] ** 2 <= 0.0)
    k, _weights = physical_shells(p.Lambda, nodes=8)
    runs = []
    endpoints = []
    for nodes in levels:
        tr = inherited_trajectory(state_path, nodes)
        modes = propagate_modes_n(tr, k, p.Lambda, p)
        endpoint = np.abs(np.asarray(modes["u"][-1, 0], dtype=np.complex128))
        endpoints.append(endpoint)
        runs.append({
            "nodes": nodes,
            "wronskian_relative_error": float(modes["wronskian_relative_error"]),
            "min_heavy_physical_omega2": float(modes["min_heavy_physical_omega2"]),
            "physical_endpoint_abs_u_min": float(np.min(endpoint)),
            "physical_endpoint_abs_u_max": float(np.max(endpoint)),
        })
    refinements = []
    for left, right in zip(endpoints[:-1], endpoints[1:]):
        refinements.append(float(np.max(np.abs(left - right) / np.maximum(np.abs(right), 1.0e-300))))
    junction_rejected = endpoint_reinitialization_rejected(state_path)
    wronskian_pass = max(run["wronskian_relative_error"] for run in runs) < 1.0e-11
    return {
        "schema": "apeiron-ap1-r2c-mode-inheritance-v1.0",
        "classification": "MODE_INHERITANCE_REQUIRED_NUMERICAL_CONTROL_OPEN",
        "state_sha256": file_sha256(state_path),
        "old_solver_or_physical_map_called": False,
        "frozen_terminal_classification_recomputed": False,
        "frequency_audit": {
            "initial_mass2": float(mass2[0]),
            "endpoint_mass2": float(mass2[-1]),
            "initial_zero_mode_omega2": float(mass2[0] - 2.25 * frozen[0, 4] ** 2),
            "endpoint_zero_mode_omega2": float(mass2[-1] - 2.25 * frozen[-1, 4] ** 2),
            "first_nonpositive_stored_index": int(first_nonpositive[0]) if len(first_nonpositive) else None,
            "endpoint_vacuum_reinitialization_rejected": junction_rejected,
        },
        "inheritance_runs": runs,
        "successive_endpoint_max_relative_changes": refinements,
        "gates": {
            "exact_PASS_state": True,
            "positive_initial_frequency": bool(mass2[0] - 2.25 * frozen[0, 4] ** 2 > 0.0),
            "continuous_inheritance_finite": bool(all(np.isfinite(list(run.values())[1:]).all() for run in runs)),
            "endpoint_reinitialization_forbidden": junction_rejected,
            "wronskian_below_1e_11": wronskian_pass,
        },
        "decision": "carry inherited modes into R2c; do not impose a new endpoint vacuum",
        "next_required_method": "rescaled or higher-precision symplectic propagation through the tachyonic band, then candidate-segment Ward and multi-resolution tests",
        "claim_boundary": "inheritance audit only; G22 remains ORANGE and no late-time observable is predicted",
    }


def main() -> None:
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("state", type=Path)
    parser.add_argument("--output", type=Path)
    args = parser.parse_args()
    rendered = json.dumps(build_inheritance_report(args.state), indent=2) + "\n"
    if args.output:
        args.output.write_text(rendered, encoding="utf-8")
    else:
        print(rendered, end="")


if __name__ == "__main__":
    main()
