"""Automated enforcement of module engineering standards (REQ-95, REQ-96)."""

import json
import os
import sys
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

ROOT = Path(__file__).resolve().parent.parent


class TestModuleStandards:
    """REQ-95/96: automated standards enforcement for all modules."""

    @pytest.fixture
    def registry(self):
        return json.load(open(ROOT / "modules" / "registry.json"))

    def test_all_l1_have_required_files(self, registry):
        for name, entry in registry.items():
            iface_path = entry["1.0.0"]["interface"]
            if not iface_path.startswith("modules/l1/"):
                continue
            l1_dir = ROOT / "modules" / "l1" / name
            assert (l1_dir / "interface.json").is_file(), f"{name}: interface.json missing"
            assert (l1_dir / "instance.json").is_file(), f"{name}: instance.json missing"
            assert (l1_dir / "README.md").is_file(), f"{name}: README.md missing"
            assert (l1_dir / "examples" / "simple.yml").is_file(), f"{name}: examples/simple.yml missing"
            assert (l1_dir / "examples" / "complex.yml").is_file(), f"{name}: examples/complex.yml missing"

    def test_all_l2_have_required_files(self, registry):
        for name, entry in registry.items():
            iface_path = entry["1.0.0"]["interface"]
            if not iface_path.startswith("modules/l2/"):
                continue
            l2_dir = ROOT / "modules" / "l2" / name
            assert (l2_dir / "composition.json").is_file(), f"{name}: composition.json missing"
            assert (l2_dir / "README.md").is_file(), f"{name}: README.md missing"
            assert (l2_dir / "examples" / "simple.yml").is_file(), f"{name}: examples/simple.yml missing"
            assert (l2_dir / "examples" / "complex.yml").is_file(), f"{name}: examples/complex.yml missing"

    def test_all_l1_have_deletion_protection_nfr(self, registry):
        for name, entry in registry.items():
            iface_path = entry["1.0.0"]["interface"]
            if not iface_path.startswith("modules/l1/"):
                continue
            iface = json.load(open(ROOT / iface_path))
            assert "deletion_protection" in iface.get("nfrs", {}), \
                f"{name}: deletion_protection NFR missing"

    def test_all_l1_have_encryption_enabled_nfr(self, registry):
        for name, entry in registry.items():
            iface_path = entry["1.0.0"]["interface"]
            if not iface_path.startswith("modules/l1/"):
                continue
            iface = json.load(open(ROOT / iface_path))
            assert "encryption_enabled" in iface.get("nfrs", {}), \
                f"{name}: encryption_enabled NFR missing"

    def test_all_l1_deletion_protection_defaults_true(self, registry):
        for name, entry in registry.items():
            iface_path = entry["1.0.0"]["interface"]
            if not iface_path.startswith("modules/l1/"):
                continue
            iface = json.load(open(ROOT / iface_path))
            dp = iface.get("nfrs", {}).get("deletion_protection", {})
            assert dp.get("default") is True, \
                f"{name}: deletion_protection default must be true"

    def test_all_l1_encryption_enabled_defaults_true(self, registry):
        for name, entry in registry.items():
            iface_path = entry["1.0.0"]["interface"]
            if not iface_path.startswith("modules/l1/"):
                continue
            iface = json.load(open(ROOT / iface_path))
            ee = iface.get("nfrs", {}).get("encryption_enabled", {})
            assert ee.get("default") is True, \
                f"{name}: encryption_enabled default must be true"

    def test_all_modules_registered(self, registry):
        l1_dirs = [d.name for d in (ROOT / "modules" / "l1").iterdir() if d.is_dir() and not d.name.startswith(".")]
        l2_dirs = [d.name for d in (ROOT / "modules" / "l2").iterdir() if d.is_dir() and not d.name.startswith(".")]
        for name in l1_dirs:
            assert name in registry, f"modules/l1/{name}/ not in registry.json"
        for name in l2_dirs:
            assert name in registry, f"modules/l2/{name}/ not in registry.json"

    def test_all_l1_readmes_have_nfrs_section(self, registry):
        """Check NFRs section for new v1.8 primitives (kms-key, uptime).
        Existing pre-v1.8 READMEs are grandfathered — the interface.json
        NFR check is the binding enforcement."""
        new_primitives = ["kms-key", "uptime"]
        for name in new_primitives:
            if name not in registry:
                continue
            readme = open(ROOT / "modules" / "l1" / name / "README.md").read()
            assert "## NFRs" in readme or "## NFR" in readme, \
                f"{name}: README.md must have a NFRs section"

    def test_standards_md_exists(self):
        assert (ROOT / "modules" / "STANDARDS.md").is_file(), "modules/STANDARDS.md must exist"

    def test_standards_md_has_required_sections(self):
        content = open(ROOT / "modules" / "STANDARDS.md").read()
        assert "L1 Primitive Standards" in content
        assert "L2 Module Standards" in content
        assert "Encryption by Default" in content
        assert "Deletion Protection by Default" in content
        assert "Code Review Checklist" in content

    def test_catalog_index_has_all_primitives(self, registry):
        content = open(ROOT / "modules" / "README.md").read()
        for name in registry:
            if registry[name]["1.0.0"]["interface"].startswith("modules/l1/"):
                assert name in content, f"modules/README.md catalog index missing primitive: {name}"

    def test_template_has_nfrs_section(self):
        content = open(ROOT / "modules" / "README-TEMPLATE.md").read()
        assert "## NFRs" in content or "## NFR" in content, "README-TEMPLATE.md must have NFRs section"