"""Exact frozen action anchor for the AP1-R3 portal-mass counterterm.

The canonical homogeneous runtime already fixes a local finite subtraction
through the triplet

    rho_ct = -C_chi2 m_chi^2 / 2,
    p_ct   = +C_chi2 m_chi^2 / 2,
    <chi^2>_ct = -C_chi2.

This module reconstructs the unique derivative-free covariant action density
that produces that triplet in the registered sign convention.  It adds no
parameter and does not evaluate or release a physical unequal-time kernel.
"""
from __future__ import annotations

from dataclasses import asdict
from hashlib import sha256
import json
import math
from pathlib import Path

import numpy as np

from chi_background_closure import (
    ChiParameters,
    flat_calibration_audit,
    frozen_renormalization_constants,
)


class CountertermAnchorError(RuntimeError):
    """The frozen runtime does not support the claimed covariant anchor."""


EXPECTED_CANONICAL_SOURCE_HASHES = {
    "model.py": "db4c0fa9cf4f2013fef299f558ca7b6b509d17c1acbd76f84ece6bd2dd0c5a65",
    "frw_noncanonical_background_v6_6.py": "f96b13df83e6d6dce8f9bec073e2aab24c23660073cee994d25a327f2b61f7f1",
    "frw_pv_fixed_mpl_renorm_v6_17.py": "aecd4dc3202604228d1a46c1a4cb439b874e129314e55378e1fbb5b999df482d",
    "frw_pv_covariant_uv_complete_v6_22.py": "9c54b4095845687b9824dab66da91031436b67a5decd01055ef487b14b4c19b7",
    "frw_pv_uv_yoshida_v6_31.py": "8fa1b93a2f24377c751f3d9fdffe700394cdb2e00842fc4dc5f35d19ae3943e1",
}

RUNTIME_MARKERS = {
    "frw_pv_fixed_mpl_renorm_v6_17.py": (
        "C=float(pre_c[0])",
        "mr=-.5*C*m2; mp=+.5*C*m2; mc=-C*np.ones_like(m2)",
    ),
    "frw_pv_covariant_uv_complete_v6_22.py": (
        "C=ren['C_chi2'];A=ren['A_g'];B=ren['B_G_rhs']",
        "mr=-.5*C*m2;mp=+.5*C*m2;mc=-C*np.ones_like(m2)",
    ),
}


def file_sha256(path: Path) -> str:
    digest = sha256()
    with path.open("rb") as stream:
        for block in iter(lambda: stream.read(1024 * 1024), b""):
            digest.update(block)
    return digest.hexdigest()


def audit_runtime_sources(source_dir: Path) -> dict[str, str]:
    observed: dict[str, str] = {}
    for name, expected in EXPECTED_CANONICAL_SOURCE_HASHES.items():
        path = source_dir / name
        if not path.is_file():
            raise CountertermAnchorError(f"missing canonical source: {name}")
        digest = file_sha256(path)
        if digest != expected:
            raise CountertermAnchorError(f"canonical source hash mismatch: {name}")
        observed[name] = digest
        for marker in RUNTIME_MARKERS.get(name, ()):
            if marker not in path.read_text(encoding="utf-8"):
                raise CountertermAnchorError(f"runtime counterterm marker missing: {name}")
    return observed


def portal_mass_derivatives(
    sigma: float,
    theta: float,
    parameters: ChiParameters | None = None,
) -> dict[str, float]:
    p = ChiParameters() if parameters is None else parameters
    p.validate()
    shifted = p.displacement_v + float(sigma)
    phase = 2.0 * float(theta) / p.phase_scale
    cosine = math.cos(phase)
    sine = math.sin(phase)
    mass2 = p.bare_mass**2 + 2.0 * p.portal_lambda * shifted**2 * cosine
    d_sigma = 4.0 * p.portal_lambda * shifted * cosine
    d_theta = -4.0 * p.portal_lambda * shifted**2 * sine / p.phase_scale
    d_sigma_sigma = 4.0 * p.portal_lambda * cosine
    d_sigma_theta = -8.0 * p.portal_lambda * shifted * sine / p.phase_scale
    d_theta_theta = (
        -8.0 * p.portal_lambda * shifted**2 * cosine / p.phase_scale**2
    )
    return {
        "mass2": mass2,
        "d_sigma": d_sigma,
        "d_theta": d_theta,
        "d_sigma_sigma": d_sigma_sigma,
        "d_sigma_theta": d_sigma_theta,
        "d_theta_sigma": d_sigma_theta,
        "d_theta_theta": d_theta_theta,
    }


def counterterm_shifts(
    sigma: float,
    theta: float,
    C_chi2: float,
    parameters: ChiParameters | None = None,
) -> dict[str, float]:
    if not math.isfinite(C_chi2):
        raise ValueError("finite frozen C_chi2 required")
    d = portal_mass_derivatives(sigma, theta, parameters)
    half_C = 0.5 * C_chi2
    potential = -half_C * d["mass2"]
    return {
        "action_lagrangian_scalar": -potential,
        "potential_energy_density": potential,
        "rho": potential,
        "pressure": -potential,
        "chi2": -C_chi2,
        "sigma_equation_rhs": half_C * d["d_sigma"],
        "theta_equation_rhs": half_C * d["d_theta"],
        "sigma_sigma_kernel": half_C * d["d_sigma_sigma"],
        "sigma_theta_kernel": half_C * d["d_sigma_theta"],
        "theta_sigma_kernel": half_C * d["d_theta_sigma"],
        "theta_theta_kernel": half_C * d["d_theta_theta"],
    }


def _mass2(sigma: float, theta: float, p: ChiParameters) -> float:
    return portal_mass_derivatives(sigma, theta, p)["mass2"]


def derivative_audit(parameters: ChiParameters | None = None) -> dict:
    p = ChiParameters() if parameters is None else parameters
    samples = (
        (0.015257914139527498, -0.69),
        (0.02, -0.61),
        (0.055, -0.76),
    )
    first_errors = []
    second_errors = []
    mixed_errors = []
    h1 = 2.0e-6
    h2 = 2.0e-4
    for sigma, theta in samples:
        d = portal_mass_derivatives(sigma, theta, p)
        numeric_sigma = (
            _mass2(sigma + h1, theta, p) - _mass2(sigma - h1, theta, p)
        ) / (2.0 * h1)
        numeric_theta = (
            _mass2(sigma, theta + h1, p) - _mass2(sigma, theta - h1, p)
        ) / (2.0 * h1)
        numeric_ss = (
            _mass2(sigma + h2, theta, p)
            - 2.0 * _mass2(sigma, theta, p)
            + _mass2(sigma - h2, theta, p)
        ) / h2**2
        numeric_tt = (
            _mass2(sigma, theta + h2, p)
            - 2.0 * _mass2(sigma, theta, p)
            + _mass2(sigma, theta - h2, p)
        ) / h2**2
        numeric_st = (
            _mass2(sigma + h2, theta + h2, p)
            - _mass2(sigma + h2, theta - h2, p)
            - _mass2(sigma - h2, theta + h2, p)
            + _mass2(sigma - h2, theta - h2, p)
        ) / (4.0 * h2**2)
        first_errors.extend(
            [
                abs(numeric_sigma - d["d_sigma"])
                / max(abs(d["d_sigma"]), 1.0e-300),
                abs(numeric_theta - d["d_theta"])
                / max(abs(d["d_theta"]), 1.0e-300),
            ]
        )
        second_errors.extend(
            [
                abs(numeric_ss - d["d_sigma_sigma"])
                / max(abs(d["d_sigma_sigma"]), 1.0e-300),
                abs(numeric_tt - d["d_theta_theta"])
                / max(abs(d["d_theta_theta"]), 1.0e-300),
            ]
        )
        mixed_errors.append(
            abs(numeric_st - d["d_sigma_theta"])
            / max(abs(d["d_sigma_theta"]), 1.0e-300)
        )
    return {
        "sample_count": len(samples),
        "max_first_derivative_relative_error": float(max(first_errors)),
        "max_pure_second_derivative_relative_error": float(max(second_errors)),
        "max_mixed_second_derivative_relative_error": float(max(mixed_errors)),
        "analytic_mixed_Hessian_symmetry_exact": True,
    }


def validate_anchor(report: dict) -> dict:
    if report.get("physical_kernel_ready") is not False:
        raise CountertermAnchorError("action anchor must not release the physical kernel")
    anchor = report.get("exact_action_anchor", {})
    if anchor.get("free_parameter_added") is not False:
        raise CountertermAnchorError("c_m anchor may not add a free parameter")
    if anchor.get("action") != (
        "S_ct,c_m = Integral d4x sqrt(-g) [+ C_chi2/2 * m_chi^2(sigma,theta)]"
    ):
        raise CountertermAnchorError("unexpected action functional")
    coefficient = report.get("frozen_coefficient", {})
    if coefficient.get("C_chi2") != coefficient.get("chi2_flat_raw"):
        raise CountertermAnchorError("C_chi2 is not the frozen flat variance subtraction")
    shifts = report.get("reference_slice_reproduction", {})
    if shifts.get("rho_plus_pressure") != 0.0:
        raise CountertermAnchorError("portal counterterm stress is not vacuum-form integrable")
    if shifts.get("chi2_shift") != -coefficient.get("C_chi2"):
        raise CountertermAnchorError("portal source subtraction has the wrong sign")
    derivatives = report.get("derivative_audit", {})
    gates = {
        "canonical_hashes_exact": len(report.get("canonical_source_sha256", {}))
        == len(EXPECTED_CANONICAL_SOURCE_HASHES),
        "runtime_triplet_reproduced": bool(
            shifts.get("runtime_rho_shift_match")
            and shifts.get("runtime_pressure_shift_match")
            and shifts.get("runtime_chi2_shift_match")
        ),
        "first_variations_match": derivatives.get(
            "max_first_derivative_relative_error", math.inf
        ) < 2.0e-8,
        "pure_second_variations_match": derivatives.get(
            "max_pure_second_derivative_relative_error", math.inf
        ) < 2.0e-6,
        "mixed_second_variation_matches": derivatives.get(
            "max_mixed_second_derivative_relative_error", math.inf
        ) < 2.0e-6,
        "portal_Hessian_symmetric": derivatives.get(
            "analytic_mixed_Hessian_symmetry_exact"
        ) is True,
        "flat_renormalization_still_passes": report.get(
            "flat_calibration", {}
        ).get("pass")
        is True,
        "no_new_parameter": anchor.get("free_parameter_added") is False,
        "physical_kernel_still_blocked": report.get("physical_kernel_ready") is False,
    }
    if not all(gates.values()):
        raise CountertermAnchorError(f"counterterm anchor gates failed: {gates}")
    return {"status": "PASS_FAIL_CLOSED", "gates": gates, "all_pass": True}


def build_anchor_report(
    canonical_source_dir: Path,
    response_spec: Path,
    a3_requirements: Path,
    canonical_spec: Path,
    prior_manifest: Path,
) -> dict:
    hashes = audit_runtime_sources(canonical_source_dir)
    texts = {
        "response_spec": response_spec.read_text(encoding="utf-8"),
        "a3_requirements": a3_requirements.read_text(encoding="utf-8"),
        "canonical_spec": canonical_spec.read_text(encoding="utf-8"),
    }
    required = {
        "response_spec": ("Pi_AB,ct", "dieselbe Parameter- und Gegenwirkungskonvention"),
        "a3_requirements": ("c_m", "keine zustandsabhängigen UV-Gegenparameter"),
        "canonical_spec": ("<χ²>_q(0) = 0", "finite R² coefficient is zero"),
    }
    for label, markers in required.items():
        missing = [marker for marker in markers if marker not in texts[label]]
        if missing:
            raise CountertermAnchorError(f"{label} missing markers: {missing}")
    prior = json.loads(prior_manifest.read_text(encoding="utf-8"))
    cm = next(
        (x for x in prior.get("local_counterterm_variations", []) if x.get("family") == "portal_mass_c_m"),
        None,
    )
    if cm is None or cm.get("status") != "DOCUMENTARY_HOLD_BLOCKS_PHYSICAL_KERNEL":
        raise CountertermAnchorError("prior manifest does not carry the required c_m hold")

    constants = frozen_renormalization_constants()
    C = float(constants["C_chi2"])
    p = ChiParameters()
    reference = portal_mass_derivatives(
        float(constants["sigma_reference"]), float(constants["theta_reference"]), p
    )
    shifts = counterterm_shifts(
        float(constants["sigma_reference"]),
        float(constants["theta_reference"]),
        C,
        p,
    )
    flat = flat_calibration_audit(p)
    report = {
        "schema": "apeiron-ap1-r3-cm-counterterm-action-anchor-v1.0",
        "classification": "EXACT_CM_ACTION_ANCHOR_PASS_PHYSICAL_KERNEL_STILL_BLOCKED",
        "authority_sha256": {
            "response_spec": file_sha256(response_spec),
            "a3_requirements": file_sha256(a3_requirements),
            "canonical_homogeneous_spec": file_sha256(canonical_spec),
            "prior_local_variation_manifest": file_sha256(prior_manifest),
        },
        "canonical_source_sha256": hashes,
        "frozen_parameters": asdict(p),
        "frozen_coefficient": {
            "C_chi2": C,
            "chi2_flat_raw": float(constants["chi2_flat_raw"]),
            "coefficient_of_m_chi_squared_in_action": 0.5 * C,
            "provenance": "hash-bound canonical flat-slice renormalization; not fitted and not newly chosen",
        },
        "exact_action_anchor": {
            "family_label": "portal_mass_c_m",
            "action": "S_ct,c_m = Integral d4x sqrt(-g) [+ C_chi2/2 * m_chi^2(sigma,theta)]",
            "equivalent_local_potential": "V_ct,c_m = - C_chi2/2 * m_chi^2(sigma,theta)",
            "variation_convention": "the convention used by the frozen background equations: -1/2 m_chi2_,A <chi2>_ren on the field-equation RHS",
            "free_parameter_added": False,
            "state_dependent": False,
        },
        "local_second_variations": {
            "metric_stress<-metric": "delta[(C_chi2/2) m_chi^2 g_mu_nu]/delta g_rho_sigma",
            "metric_stress<-sigma": "+(C_chi2/2) m_chi2_,sigma g_mu_nu delta4",
            "metric_stress<-theta": "+(C_chi2/2) m_chi2_,theta g_mu_nu delta4",
            "sigma_portal<-metric": "metric-volume variation of +(C_chi2/2) m_chi2_,sigma",
            "theta_portal<-metric": "metric-volume variation of +(C_chi2/2) m_chi2_,theta",
            "sigma_portal<-sigma": "+(C_chi2/2) m_chi2_,sigma_sigma delta4",
            "sigma_portal<-theta": "+(C_chi2/2) m_chi2_,sigma_theta delta4",
            "theta_portal<-sigma": "+(C_chi2/2) m_chi2_,theta_sigma delta4",
            "theta_portal<-theta": "+(C_chi2/2) m_chi2_,theta_theta delta4",
        },
        "reference_slice_reproduction": {
            "mass2": reference["mass2"],
            "rho_shift": shifts["rho"],
            "pressure_shift": shifts["pressure"],
            "chi2_shift": shifts["chi2"],
            "rho_plus_pressure": shifts["rho"] + shifts["pressure"],
            "runtime_rho_shift_match": shifts["rho"] == -0.5 * C * reference["mass2"],
            "runtime_pressure_shift_match": shifts["pressure"] == 0.5 * C * reference["mass2"],
            "runtime_chi2_shift_match": shifts["chi2"] == -C,
            "local_Ward_form": "nabla^mu T_ct,mu_nu - J_ct,sigma partial_nu sigma - J_ct,theta partial_nu theta = 0",
        },
        "derivative_audit": derivative_audit(p),
        "flat_calibration": {
            "pass": bool(all(flat["gates"].values())),
            "gates": flat["gates"],
        },
        "physical_kernel_ready": False,
        "remaining_blockers": [
            "new_AP1_M1_background",
            "component_and_gauge_invariant_scalar_projection",
            "pilot_derived_physical_tolerances_frozen_in_advance",
        ],
        "claim_boundary": "exact local c_m action anchor only; no physical unequal-time kernel, Ward result, observable, fit, or significance",
    }
    report["validation"] = validate_anchor(report)
    return report


def main() -> None:
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument("canonical_source_dir", type=Path)
    parser.add_argument("response_spec", type=Path)
    parser.add_argument("a3_requirements", type=Path)
    parser.add_argument("canonical_spec", type=Path)
    parser.add_argument("prior_manifest", type=Path)
    parser.add_argument("--output", type=Path)
    args = parser.parse_args()
    report = build_anchor_report(
        args.canonical_source_dir,
        args.response_spec,
        args.a3_requirements,
        args.canonical_spec,
        args.prior_manifest,
    )
    rendered = json.dumps(report, indent=2, ensure_ascii=False) + "\n"
    if args.output is None:
        print(rendered, end="")
    else:
        args.output.write_text(rendered, encoding="utf-8")


if __name__ == "__main__":
    main()
