"""
Test Suite for DSS Context Compiler
Validates all core functionality: cascade merging, token resolution, security, and error handling.
"""

import json
import os
import sys
from pathlib import Path

# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))

from core import (
    ContextCompiler,
    get_active_context,
    resolve_token,
    validate_manifest,
    list_skins,
    get_compiler_status,
    EMERGENCY_SKIN
)


class TestContextCompiler:
    """Test suite for Context Compiler"""

    def __init__(self):
        self.base_dir = Path(__file__).parent.parent
        self.skins_dir = self.base_dir / "core" / "skins"
        self.admin_manifest = self.base_dir.parent / "admin-ui" / "ds.config.json"
        self.compiler = ContextCompiler(skins_dir=str(self.skins_dir))
        self.passed = 0
        self.failed = 0

    def assert_equal(self, actual, expected, message):
        """Simple assertion helper"""
        if actual == expected:
            print(f"✓ {message}")
            self.passed += 1
            return True
        else:
            print(f"✗ {message}")
            print(f"  Expected: {expected}")
            print(f"  Actual: {actual}")
            self.failed += 1
            return False

    def assert_true(self, condition, message):
        """Assert condition is true"""
        if condition:
            print(f"✓ {message}")
            self.passed += 1
            return True
        else:
            print(f"✗ {message}")
            self.failed += 1
            return False

    def assert_in(self, needle, haystack, message):
        """Assert needle is in haystack"""
        if needle in haystack:
            print(f"✓ {message}")
            self.passed += 1
            return True
        else:
            print(f"✗ {message}")
            print(f"  '{needle}' not found in {haystack}")
            self.failed += 1
            return False

    def test_basic_compilation(self):
        """Test 1: Basic 3-layer cascade compilation"""
        print("\n=== Test 1: Basic Compilation (3-Layer Cascade) ===")

        try:
            context = self.compiler.compile(str(self.admin_manifest))

            # Test project override (Layer 3)
            self.assert_equal(
                context.get("tokens", {}).get("colors", {}).get("primary"),
                "#6366f1",
                "Project override applied correctly (colors.primary)"
            )

            # Test skin value (Layer 2 - workbench)
            self.assert_equal(
                context.get("tokens", {}).get("colors", {}).get("background"),
                "#0F172A",
                "Workbench skin value inherited (colors.background)"
            )

            # Test base value (Layer 1)
            self.assert_equal(
                context.get("tokens", {}).get("spacing", {}).get("0"),
                "0px",
                "Base skin value inherited (spacing.0)"
            )

            # Test metadata injection
            self.assert_in("_meta", context, "Metadata injected into context")
            self.assert_equal(
                context.get("_meta", {}).get("project_id"),
                "dss-admin",
                "Project ID in metadata"
            )

        except Exception as e:
            print(f"✗ Basic compilation failed with error: {e}")
            self.failed += 1

    def test_debug_provenance(self):
        """Test 2: Debug provenance tracking"""
        print("\n=== Test 2: Debug Provenance Tracking ===")

        try:
            context = self.compiler.compile(str(self.admin_manifest), debug=True)

            self.assert_in("_provenance", context, "Provenance data included in debug mode")
            self.assert_true(
                isinstance(context.get("_provenance", []), list),
                "Provenance is a list"
            )
            self.assert_true(
                len(context.get("_provenance", [])) > 0,
                "Provenance contains tracking entries"
            )

        except Exception as e:
            print(f"✗ Debug provenance test failed with error: {e}")
            self.failed += 1

    def test_token_resolution(self):
        """Test 3: Token resolution via MCP tool"""
        print("\n=== Test 3: Token Resolution ===")

        try:
            # Test project override token
            result = resolve_token(str(self.admin_manifest), "colors.primary")
            self.assert_equal(result, "#6366f1", "Resolved project override token")

            # Test skin-level token
            result = resolve_token(str(self.admin_manifest), "colors.background")
            self.assert_equal(result, "#0F172A", "Resolved skin-level token")

            # Test base-level token
            result = resolve_token(str(self.admin_manifest), "spacing.0")
            self.assert_equal(result, "0px", "Resolved base-level token")

            # Test nested token
            result = resolve_token(str(self.admin_manifest), "typography.fontFamily.sans")
            self.assert_true(
                "Inter" in result or "system-ui" in result,
                "Resolved nested token"
            )

            # Test non-existent token
            result = resolve_token(str(self.admin_manifest), "nonexistent.token")
            self.assert_in("not found", result, "Non-existent token returns error message")

        except Exception as e:
            print(f"✗ Token resolution test failed with error: {e}")
            self.failed += 1

    def test_skin_listing(self):
        """Test 4: Skin listing functionality"""
        print("\n=== Test 4: Skin Listing ===")

        try:
            skins_json = list_skins()
            skins = json.loads(skins_json)

            self.assert_in("base", skins, "Base skin listed")
            self.assert_in("classic", skins, "Classic skin listed")
            self.assert_in("workbench", skins, "Workbench skin listed")
            self.assert_true(len(skins) >= 3, "At least 3 skins available")

        except Exception as e:
            print(f"✗ Skin listing test failed with error: {e}")
            self.failed += 1

    def test_safe_boot_protocol(self):
        """Test 5: Safe Boot Protocol (emergency fallback)"""
        print("\n=== Test 5: Safe Boot Protocol ===")

        try:
            # Test with non-existent manifest
            context = self.compiler.compile("/nonexistent/path.json")

            self.assert_equal(
                context.get("status"),
                "emergency_mode",
                "Emergency mode activated for invalid path"
            )

            self.assert_in("_error", context, "Error details included in safe boot")

            # Validate emergency skin has required structure
            self.assert_in("tokens", context, "Emergency skin has tokens")
            self.assert_in("colors", context.get("tokens", {}), "Emergency skin has colors")
            self.assert_in("primary", context.get("tokens", {}).get("colors", {}), "Emergency skin has primary color")

        except Exception as e:
            print(f"✗ Safe Boot Protocol test failed with error: {e}")
            self.failed += 1

    def test_path_traversal_prevention(self):
        """Test 6: Security - Path traversal prevention"""
        print("\n=== Test 6: Path Traversal Prevention (Security) ===")

        try:
            # Attempt path traversal attack
            try:
                self.compiler._load_skin("../../etc/passwd")
                print("✗ Path traversal not prevented!")
                self.failed += 1
            except ValueError as e:
                self.assert_in(
                    "path traversal",
                    str(e).lower(),
                    "Path traversal attack blocked"
                )

            # Attempt another variant
            try:
                self.compiler._load_skin("../../../root/.ssh/id_rsa")
                print("✗ Path traversal variant not prevented!")
                self.failed += 1
            except ValueError as e:
                self.assert_in(
                    "path traversal",
                    str(e).lower(),
                    "Path traversal variant blocked"
                )

        except Exception as e:
            print(f"✗ Path traversal prevention test failed with unexpected error: {e}")
            self.failed += 1

    def test_compiler_status(self):
        """Bonus Test: Compiler status tool"""
        print("\n=== Bonus Test: Compiler Status ===")

        try:
            status_json = get_compiler_status()
            status = json.loads(status_json)

            self.assert_equal(status.get("status"), "active", "Compiler status is active")
            self.assert_in("skins_directory", status, "Status includes skins directory")
            self.assert_in("safe_boot_ready", status, "Status confirms Safe Boot ready")

        except Exception as e:
            print(f"✗ Compiler status test failed with error: {e}")
            self.failed += 1

    def run_all_tests(self):
        """Execute all tests and report results"""
        print("=" * 60)
        print("DSS Context Compiler Test Suite")
        print("=" * 60)

        self.test_basic_compilation()
        self.test_debug_provenance()
        self.test_token_resolution()
        self.test_skin_listing()
        self.test_safe_boot_protocol()
        self.test_path_traversal_prevention()
        self.test_compiler_status()

        print("\n" + "=" * 60)
        print(f"Test Results: {self.passed} passed, {self.failed} failed")
        print("=" * 60)

        if self.failed == 0:
            print("✓ ALL TESTS PASSED - Ready for production deployment")
            return True
        else:
            print("✗ SOME TESTS FAILED - Review errors before deploying")
            return False


if __name__ == "__main__":
    tester = TestContextCompiler()
    success = tester.run_all_tests()
    sys.exit(0 if success else 1)
