"""Validate that no proprietary IP has leaked into the harness.

This script checks for prohibited patterns, file references, and other
indicators that would reveal proprietary architecture details.
"""

from __future__ import annotations

import argparse
import re
import sys
from dataclasses import dataclass, field
from pathlib import Path


@dataclass
class ValidationResult:
    """Results from validation checks.

    Attributes:
        passed: Whether all checks passed.
        errors: List of validation errors.
        warnings: List of validation warnings.
        files_checked: Number of files checked.
    """

    passed: bool = True
    errors: list[str] = field(default_factory=list)
    warnings: list[str] = field(default_factory=list)
    files_checked: int = 0


# Patterns that indicate proprietary IP leakage
PROHIBITED_PATTERNS = [
    # Graph Neural Network architecture
    (r"\bGAT\b", "GAT (Graph Attention Network)"),
    (r"\bGATv2\b", "GATv2"),
    (r"\bGATConv\b", "GATConv"),
    (r"\bgraph_nn\b", "graph_nn"),
    (r"\btorch_geometric\b", "torch_geometric"),
    (r"\bMessagePassing\b", "MessagePassing"),
    # Bipartite/heterogeneous graph
    (r"\bbipartite\b", "bipartite graph"),
    (r"\bheterogeneous\b", "heterogeneous graph"),
    (r"\bedge_index\b", "edge_index"),
    (r"\bHeteroData\b", "HeteroData"),
    # Tier/difficulty routing logic
    (r"\btier\b", "tier classification"),
    (r"\btiered\b", "tiered routing"),
    (r"\bdifficulty[._]signal\b", "difficulty signal"),
    (r"\bmean_logit\b", "mean_logit"),
    # Ensemble/judge logic
    (r"\bensemble\b", "ensemble logic"),
    (r"\bjudge\b", "judge model"),
    (r"\bcandidate\b", "candidate selection"),
    # Product names
    (r"\beb1\b", "eb1 product name", True),  # Case insensitive
    (r"\bEB1\b", "EB1 product name"),
    # Internal module references
    (r"\bgepa\b", "GEPA framework", True),
    (r"\bembergraphrouter\b", "embergraphrouter module", True),
    (r"\btakumi\b", "takumi module", True),
    # Routing instruction conditioning
    (r"instruction[._]condition", "instruction conditioning"),
    (r"routing[._]instruction", "routing instruction"),
    # Conductor orchestration logic (EB1-specific)
    (r"\bDECOMPOSE\b", "DECOMPOSE action keyword"),
    (r"\bSYNTHESIZE\b", "SYNTHESIZE action keyword"),
    (r"\bDELEGATE\b", "DELEGATE action keyword"),
    (r"\brouting_threshold\b", "routing threshold tuning"),
    (r"\bbypass_criteria\b", "bypass criteria"),
    (r"\bconfidence_threshold\b", "confidence threshold tuning"),
    (r"\bentropy_threshold\b", "entropy threshold tuning"),
    (r"\bTwoStageOrchestrator\b", "TwoStageOrchestrator pattern"),
    (r"\bEB1Conductor\b", "EB1Conductor class"),
    (r"\bsubtask_parallelism\b", "subtask parallelism"),
]

# Files that should not exist in the harness
PROHIBITED_FILES = [
    "embergraphrouter",
    "takumi",
    "eb1_alpha",
    "eb1_gnn",
    "gpt5",
    "gepa",
]

# Allowed exceptions (files where certain patterns are expected)
ALLOWED_EXCEPTIONS = {
    "validate_no_leakage.py": ["all patterns"],  # This file itself
    "README.md": ["eb1"],  # May mention eb1 in context
    # Conductor infrastructure files - generic abstractions are allowed
    "interface.py": ["ConductorInterface"],  # Abstract interface
    "rnd1_conductor.py": ["RND1Conductor"],  # Adapter class name
    "claude_conductor.py": ["ClaudeConductor"],  # Adapter class name
    # Graph router files - educational architecture documentation for partners
    "hetero_graph.py": ["all patterns"],  # Decoded graph architecture
    "graph_router.py": ["all patterns"],  # Graph-based inference pipeline
    # Module docstrings that reference the architecture
    "__init__.py": ["bipartite graph", "heterogeneous graph", "GAT (Graph Attention Network)"],
    "model/__init__.py": ["bipartite graph", "heterogeneous graph", "GAT (Graph Attention Network)"],
    "inference/__init__.py": ["bipartite graph", "heterogeneous graph", "GAT (Graph Attention Network)"],
}

# Directories to skip (internal tooling, not shipped to partners)
INTERNAL_DIRECTORIES = {"tests", ".venv", "venv", "__pycache__", ".git", "node_modules"}


def check_file_content(file_path: Path, harness_root: Path) -> list[str]:
    """Check a file for prohibited patterns.

    Args:
        file_path: Path to the file to check.
        harness_root: Root directory of the harness.

    Returns:
        List of error messages.
    """
    errors = []
    rel_path = str(file_path.relative_to(harness_root))

    # Skip internal directories (not shipped to partners)
    for internal_dir in INTERNAL_DIRECTORIES:
        if rel_path.startswith(internal_dir + "/") or rel_path == internal_dir:
            return []

    # Check if this file has exceptions (by filename or relative path)
    exceptions = ALLOWED_EXCEPTIONS.get(file_path.name, [])
    exceptions = exceptions or ALLOWED_EXCEPTIONS.get(rel_path, [])
    if "all patterns" in exceptions:
        return []

    try:
        content = file_path.read_text()
    except Exception as e:
        return [f"{rel_path}: Could not read file: {e}"]

    for pattern_tuple in PROHIBITED_PATTERNS:
        if len(pattern_tuple) == 3:
            pattern, description, case_insensitive = pattern_tuple
            flags = re.IGNORECASE if case_insensitive else 0
        else:
            pattern, description = pattern_tuple
            flags = 0

        # Skip if this pattern is allowed for this file
        if description in exceptions or pattern in exceptions:
            continue

        matches = re.findall(pattern, content, flags)
        if matches:
            # Find line numbers for context
            lines = content.split("\n")
            for i, line in enumerate(lines, 1):
                if re.search(pattern, line, flags):
                    errors.append(
                        f"{rel_path}:{i}: Found prohibited pattern '{description}'"
                    )

    return errors


def check_directory_structure(harness_root: Path) -> list[str]:
    """Check for prohibited directories or files.

    Args:
        harness_root: Root directory of the harness.

    Returns:
        List of error messages.
    """
    errors = []

    for prohibited in PROHIBITED_FILES:
        # Check for directories
        for match in harness_root.rglob(f"*{prohibited}*"):
            if match.is_dir() or match.is_file():
                rel_path = match.relative_to(harness_root)
                errors.append(f"Prohibited path found: {rel_path}")

    return errors


def validate_harness(harness_root: Path) -> ValidationResult:
    """Run all validation checks on the harness.

    Args:
        harness_root: Root directory of the harness.

    Returns:
        ValidationResult with check outcomes.
    """
    result = ValidationResult()

    if not harness_root.exists():
        result.passed = False
        result.errors.append(f"Harness directory does not exist: {harness_root}")
        return result

    # Check directory structure
    dir_errors = check_directory_structure(harness_root)
    result.errors.extend(dir_errors)

    # Check file contents
    file_patterns = ["*.py", "*.yaml", "*.yml", "*.json", "*.md", "*.txt"]
    for pattern in file_patterns:
        for file_path in harness_root.rglob(pattern):
            if file_path.is_file():
                result.files_checked += 1
                file_errors = check_file_content(file_path, harness_root)
                result.errors.extend(file_errors)

    # Check for Python imports that shouldn't exist
    for py_file in harness_root.rglob("*.py"):
        if py_file.name == "validate_no_leakage.py":
            continue

        try:
            content = py_file.read_text()
        except UnicodeDecodeError:
            # Skip binary files
            continue

        # Check for imports from prohibited modules
        prohibited_imports = [
            r"from\s+ember\.gateway",
            r"from\s+ember\.takumi",
            r"from\s+ember\.eb1",
            r"from\s+embergraphrouter",
            r"import\s+torch_geometric",
        ]

        for import_pattern in prohibited_imports:
            if re.search(import_pattern, content):
                rel_path = py_file.relative_to(harness_root)
                result.errors.append(
                    f"{rel_path}: Contains prohibited import matching '{import_pattern}'"
                )

    result.passed = len(result.errors) == 0
    return result


def print_results(result: ValidationResult) -> None:
    """Print validation results."""
    print("\n" + "=" * 60)
    print("IP LEAKAGE VALIDATION RESULTS")
    print("=" * 60)
    print(f"Files checked: {result.files_checked}")

    if result.passed:
        print("\n[PASS] No IP leakage detected")
    else:
        print(f"\n[FAIL] Found {len(result.errors)} issues:")
        for error in result.errors:
            print(f"  - {error}")

    if result.warnings:
        print(f"\nWarnings ({len(result.warnings)}):")
        for warning in result.warnings:
            print(f"  - {warning}")

    print("=" * 60)


def main() -> int:
    """CLI entry point."""
    parser = argparse.ArgumentParser(
        description="Validate the harness for IP leakage"
    )
    parser.add_argument(
        "--harness-dir",
        type=Path,
        default=Path(__file__).parent.parent,
        help="Root directory of the harness to validate",
    )
    parser.add_argument(
        "--strict",
        action="store_true",
        help="Treat warnings as errors",
    )

    args = parser.parse_args()

    result = validate_harness(args.harness_dir)
    print_results(result)

    if args.strict and result.warnings:
        return 1

    return 0 if result.passed else 1


if __name__ == "__main__":
    sys.exit(main())
