# SPDX-License-Identifier: MIT
"""Fail-closed checksum, source, symlink, license, and optimized-mode tests."""
import copy
import hashlib
import json
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
REPO = ROOT.parent


class CheckerTamperTests(unittest.TestCase):
    def fixture(self, directory):
        repo = Path(directory) / "repo"
        package = repo / "v3-open-spec"
        shutil.copytree(ROOT, package, ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "*.pyo"))
        shutil.copy2(REPO / "LICENSE", repo / "LICENSE")
        mapping = json.loads((package / "mappings/canonical-v3.json").read_text("utf-8"))
        for binding in mapping["canonicalSources"].values():
            source = REPO / binding["path"]
            target = repo / binding["path"]
            target.parent.mkdir(parents=True, exist_ok=True)
            if source.resolve() != (REPO / "LICENSE").resolve():
                shutil.copy2(source, target)
        supported = json.loads((REPO / "v3-migration/completeness/supported-v3-modules.json").read_text("utf-8"))
        for item in supported["modules"]:
            source = REPO / item["path"]
            target = repo / item["path"]
            target.parent.mkdir(parents=True, exist_ok=True)
            shutil.copy2(source, target)
        for rel in ("mappings/canonical-v3.json", "interfaces/canonical-v3-interfaces.v1.json"):
            path = package / rel
            data = json.loads(path.read_text("utf-8"))
            for item in data["supportedSourceModules"]:
                item["sha256"] = hashlib.sha256((repo / item["path"]).read_bytes()).hexdigest()
            path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n")
        subprocess.run([sys.executable, str(package / "tools/check.py"), "--write"], cwd=package, text=True, capture_output=True)
        return repo, package, mapping

    def run_checker(self, package, optimized=False):
        command = [sys.executable]
        if optimized:
            command.append("-O")
        command.append(str(package / "tools/check.py"))
        return subprocess.run(command, cwd=package, text=True, capture_output=True)

    def test_checksum_tamper_fails_in_normal_and_optimized_modes(self):
        with tempfile.TemporaryDirectory() as directory:
            _, package, _ = self.fixture(directory)
            (package / "README.md").write_text((package / "README.md").read_text() + "\ntamper\n")
            for optimized in (False, True):
                with self.subTest(optimized=optimized):
                    run = self.run_checker(package, optimized)
                    self.assertNotEqual(run.returncode, 0)
                    self.assertIn("artifact manifest drift", run.stderr)

    def test_source_binding_tamper_fails_closed(self):
        with tempfile.TemporaryDirectory() as directory:
            repo, package, mapping = self.fixture(directory)
            binding = mapping["canonicalSources"]["types_ts"]
            (repo / binding["path"]).write_text("tamper")
            run = self.run_checker(package)
            self.assertNotEqual(run.returncode, 0)
            self.assertIn("source digest drift", run.stderr)

    def test_license_tamper_fails_closed(self):
        with tempfile.TemporaryDirectory() as directory:
            repo, package, _ = self.fixture(directory)
            (repo / "LICENSE").write_text("wrong license\n")
            run = self.run_checker(package)
            self.assertNotEqual(run.returncode, 0)
            self.assertTrue(
                "VF3-LICENSE-001" in run.stderr
                or "artifact manifest drift" in run.stderr
            )

    def test_package_symlink_fails_closed(self):
        with tempfile.TemporaryDirectory() as directory:
            _, package, _ = self.fixture(directory)
            (package / "symlink").symlink_to(package / "README.md")
            run = self.run_checker(package)
            self.assertNotEqual(run.returncode, 0)
            self.assertIn("symlinks are prohibited", run.stderr)

    def test_missing_manifest_fails_closed(self):
        with tempfile.TemporaryDirectory() as directory:
            _, package, _ = self.fixture(directory)
            (package / "ARTIFACTS.sha256").unlink()
            run = self.run_checker(package)
            self.assertNotEqual(run.returncode, 0)
            self.assertIn("artifact manifest drift", run.stderr)


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