from __future__ import annotations

from copy import deepcopy
import os
from pathlib import Path
import tempfile
import unittest

from ap1_r3_local_variation_manifest import (
    LocalVariationManifestError,
    REQUIRED_COUNTERTERM_FAMILIES,
    REQUIRED_PORTAL_CONTACTS,
    build_manifest,
    validate_manifest,
)


ROOT = Path(os.environ["APEIRON_AP1_ROOT"])
SPEC = ROOT / "AP1" / "APEIRON_AP1_RESPONSE_KERNEL_SPEC_LATEST.md"
A3 = ROOT / "THEORY" / "APEIRON_PHASE_A3_SEMICLASSICAL_RESPONSE_REQUIREMENTS_LATEST.md"
CANONICAL = ROOT / "PUBLICATION" / "APEIRON_CANONICAL_MODEL_SPECIFICATION_LATEST.md"
CM_ANCHOR = ROOT / "AP1" / "APEIRON_AP1_R3_CM_COUNTERTERM_ANCHOR_LATEST.json"


class AP1R3LocalVariationManifestTests(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.manifest = build_manifest(SPEC, A3, CANONICAL, CM_ANCHOR)

    def test_manifest_is_complete_but_does_not_release_physical_kernel(self):
        self.assertEqual(self.manifest["validation"]["status"], "PASS_FAIL_CLOSED")
        self.assertFalse(self.manifest["physical_kernel_ready"])

    def test_all_required_counterterm_families_are_explicit(self):
        families = {entry["family"] for entry in self.manifest["local_counterterm_variations"]}
        self.assertEqual(families, set(REQUIRED_COUNTERTERM_FAMILIES))

    def test_portal_Hessian_is_a_complete_two_by_two_contact_block(self):
        pairs = {
            (entry.get("row_source"), entry.get("column_perturbation"))
            for entry in self.manifest["local_contact_variations"]
            if entry.get("origin") == "portal_mass_Hessian"
        }
        self.assertEqual(pairs, set(REQUIRED_PORTAL_CONTACTS))

    def test_cross_portal_mass_derivatives_are_identical(self):
        entries = {entry["id"]: entry for entry in self.manifest["local_contact_variations"]}
        self.assertEqual(entries["C_SIGMA_THETA"]["mass_derivative"], entries["C_THETA_SIGMA"]["mass_derivative"])

    def test_initial_state_variation_remains_separate_and_has_no_UV_role(self):
        state = self.manifest["initial_state_variation"]
        self.assertTrue(state["separate_from_local_manifest"])
        self.assertEqual(state["UV_counterterm_role"], "none")

    def test_no_counterterm_coefficient_is_invented(self):
        for entry in self.manifest["local_counterterm_variations"]:
            self.assertFalse(entry["coefficient_value_supplied_here"])
            self.assertFalse(entry["free_parameter_added"])

    def test_c_m_functional_is_anchored_but_physical_kernel_stays_blocked(self):
        entry = next(
            item for item in self.manifest["local_counterterm_variations"]
            if item["family"] == "portal_mass_c_m"
        )
        self.assertEqual(
            entry["status"],
            "ACTION_FUNCTIONAL_ANCHORED_PHYSICAL_BACKGROUND_STILL_BLOCKED",
        )
        self.assertNotIn(
            "exact_S_ct,c_m_functional_anchor",
            self.manifest["blocked_before_physical_evaluation"],
        )
        self.assertIn("new_AP1_M1_background", self.manifest["blocked_before_physical_evaluation"])

    def test_missing_counterterm_family_fails_closed(self):
        candidate = deepcopy(self.manifest)
        candidate["local_counterterm_variations"] = candidate["local_counterterm_variations"][:-1]
        with self.assertRaises(LocalVariationManifestError):
            validate_manifest(candidate)

    def test_duplicate_variation_identifier_fails_closed(self):
        candidate = deepcopy(self.manifest)
        candidate["local_counterterm_variations"][0]["id"] = candidate["local_contact_variations"][0]["id"]
        with self.assertRaises(LocalVariationManifestError):
            validate_manifest(candidate)

    def test_missing_authority_marker_fails_closed(self):
        with tempfile.TemporaryDirectory() as directory:
            broken = Path(directory) / "a3.md"
            broken.write_text(A3.read_text(encoding="utf-8").replace("c_m", "removed_marker"), encoding="utf-8")
            with self.assertRaises(LocalVariationManifestError):
                build_manifest(SPEC, broken, CANONICAL, CM_ANCHOR)

    def test_invalid_c_m_anchor_fails_closed(self):
        with tempfile.TemporaryDirectory() as directory:
            broken = Path(directory) / "anchor.json"
            broken.write_text(
                CM_ANCHOR.read_text(encoding="utf-8").replace(
                    '"all_pass": true', '"all_pass": false'
                ),
                encoding="utf-8",
            )
            with self.assertRaises(LocalVariationManifestError):
                build_manifest(SPEC, A3, CANONICAL, broken)


if __name__ == "__main__":
    unittest.main()
