from __future__ import annotations

import json
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
CHECK = ROOT / "scripts/validate_partner_package.py"


def run(relative: str | None = None, mutate=None):
    """Run in an isolated cache-free copy; never mutate the working package."""
    with tempfile.TemporaryDirectory() as td:
        copied = Path(td) / "partner-package"
        shutil.copytree(
            ROOT, copied,
            ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "*.pyo"),
        )
        if relative is not None:
            path = copied / relative
            item = json.loads(path.read_text(encoding="utf-8"))
            mutate(item)
            path.write_text(json.dumps(item), encoding="utf-8")
        return subprocess.run(
            [sys.executable, "-B", str(copied / "scripts/validate_partner_package.py")],
            cwd=copied, text=True, capture_output=True, check=False,
        )


class PartnerPackageTests(unittest.TestCase):
    def test_package_validates_fail_closed(self):
        result = run()
        self.assertEqual(result.returncode, 0, result.stderr)
        self.assertIn('"pilotReady": false', result.stdout)
        self.assertIn('"proposalTemplateCount": 8', result.stdout)

    def test_cache_absent_normally_and_injected_cache_is_rejected(self):
        existing = [
            path for path in ROOT.rglob("*")
            if path.is_file()
            and ("__pycache__" in path.parts or path.suffix in {".pyc", ".pyo"})
        ]
        self.assertEqual(existing, [])
        with tempfile.TemporaryDirectory() as td:
            copied = Path(td) / "partner-package"
            shutil.copytree(ROOT, copied)
            injected = copied / "scripts/__pycache__/injected.pyc"
            injected.parent.mkdir(parents=True)
            injected.write_bytes(b"not-bytecode")
            result = subprocess.run(
                [sys.executable, "-B", str(copied / "scripts/validate_partner_package.py")],
                cwd=copied, text=True, capture_output=True, check=False,
            )
        self.assertNotEqual(result.returncode, 0)
        self.assertIn("unexpected cache/generated files", result.stderr)

    def test_false_pilot_ready_is_rejected(self):
        result = run(
            "manifests/base-v3-deployment-manifest.template.json",
            lambda item: item.__setitem__("pilotReady", True),
        )
        self.assertNotEqual(result.returncode, 0)

    def test_v2_default_change_is_rejected(self):
        result = run(
            "manifests/base-v3-deployment-manifest.template.json",
            lambda item: item.__setitem__("v2Default", False),
        )
        self.assertNotEqual(result.returncode, 0)

    def test_bundling_is_rejected(self):
        result = run(
            "proposals/05-module-route.json",
            lambda item: item.__setitem__("actionClass", "caps"),
        )
        self.assertNotEqual(result.returncode, 0)

    def test_populated_target_is_rejected(self):
        result = run(
            "proposals/05-module-route.json",
            lambda item: item["action"].__setitem__(
                "target", "0x0000000000000000000000000000000000000001"
            ),
        )
        self.assertNotEqual(result.returncode, 0)

    def test_invented_role_is_rejected(self):
        result = run(
            "manifests/base-v3-deployment-manifest.template.json",
            lambda item: item["roles"].__setitem__("attesters", ["invented"]),
        )
        self.assertNotEqual(result.returncode, 0)

    def test_partner_limit_or_pilot_evidence_is_rejected(self):
        result = run(
            "manifests/base-v3-deployment-manifest.template.json",
            lambda item: item["pilotTruthfulness"].__setitem__("activeDesignPartnerLimit", 2),
        )
        self.assertNotEqual(result.returncode, 0)
        result = run(
            "manifests/base-v3-deployment-manifest.template.json",
            lambda item: item["evidencePlaceholders"].__setitem__(
                "baseSepoliaRehearsal", "invented"
            ),
        )
        self.assertNotEqual(result.returncode, 0)

    def test_missing_approval_phrase_is_rejected(self):
        result = run(
            "proposals/06-caps.json",
            lambda item: item.__setitem__(
                "approvalRequirements", item["approvalRequirements"][:2]
            ),
        )
        self.assertNotEqual(result.returncode, 0)


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