"""
Codebase Capability Analyzer

Scans the Atomizer codebase to build a capability index showing what features
are already implemented. This enables intelligent gap detection.

Author: Atomizer Development Team
Version: 0.1.0 (Phase 2.5)
Last Updated: 2025-01-16
"""

import ast
import re
from pathlib import Path
from typing import Dict, List, Set, Any, Optional
from dataclasses import dataclass


@dataclass
class CodeCapability:
    """Represents a discovered capability in the codebase."""
    name: str
    category: str
    file_path: Path
    confidence: float
    details: Dict[str, Any]


class CodebaseCapabilityAnalyzer:
    """Analyzes the Atomizer codebase to identify existing capabilities."""

    def __init__(self, project_root: Optional[Path] = None):
        if project_root is None:
            # Auto-detect project root
            current = Path(__file__).resolve()
            while current.parent != current:
                if (current / 'optimization_engine').exists():
                    project_root = current
                    break
                current = current.parent

        self.project_root = project_root
        self.capabilities: Dict[str, Dict[str, Any]] = {}

    def analyze_codebase(self) -> Dict[str, Any]:
        """
        Analyze the entire codebase and build capability index.

        Returns:
            {
                'optimization': {
                    'optuna_integration': True,
                    'parameter_updating': True,
                    'expression_parsing': True
                },
                'simulation': {
                    'nx_solver': True,
                    'sol101': True,
                    'sol103': False
                },
                'result_extraction': {
                    'displacement': True,
                    'stress': True,
                    'strain': False
                },
                'geometry': {
                    'parameter_extraction': True,
                    'expression_filtering': True
                },
                'materials': {
                    'xml_generation': True
                }
            }
        """
        capabilities = {
            'optimization': {},
            'simulation': {},
            'result_extraction': {},
            'geometry': {},
            'materials': {},
            'loads_bc': {},
            'mesh': {},
            'reporting': {}
        }

        # Analyze optimization capabilities
        capabilities['optimization'] = self._analyze_optimization()

        # Analyze simulation capabilities
        capabilities['simulation'] = self._analyze_simulation()

        # Analyze result extraction capabilities
        capabilities['result_extraction'] = self._analyze_result_extraction()

        # Analyze geometry capabilities
        capabilities['geometry'] = self._analyze_geometry()

        # Analyze material capabilities
        capabilities['materials'] = self._analyze_materials()

        self.capabilities = capabilities
        return capabilities

    def _analyze_optimization(self) -> Dict[str, bool]:
        """Analyze optimization-related capabilities."""
        capabilities = {
            'optuna_integration': False,
            'parameter_updating': False,
            'expression_parsing': False,
            'history_tracking': False
        }

        # Check for Optuna integration
        optuna_files = list(self.project_root.glob('optimization_engine/*optuna*.py'))
        if optuna_files or self._file_contains_pattern(
            self.project_root / 'optimization_engine',
            r'import\s+optuna|from\s+optuna'
        ):
            capabilities['optuna_integration'] = True

        # Check for parameter updating
        if self._file_contains_pattern(
            self.project_root / 'optimization_engine',
            r'def\s+update_parameter|class\s+\w*Parameter\w*Updater'
        ):
            capabilities['parameter_updating'] = True

        # Check for expression parsing
        if self._file_contains_pattern(
            self.project_root / 'optimization_engine',
            r'def\s+parse_expression|def\s+extract.*expression'
        ):
            capabilities['expression_parsing'] = True

        # Check for history tracking
        if self._file_contains_pattern(
            self.project_root / 'optimization_engine',
            r'class\s+\w*History|def\s+track_history'
        ):
            capabilities['history_tracking'] = True

        return capabilities

    def _analyze_simulation(self) -> Dict[str, bool]:
        """Analyze simulation-related capabilities."""
        capabilities = {
            'nx_solver': False,
            'sol101': False,
            'sol103': False,
            'sol106': False,
            'journal_execution': False
        }

        # Check for NX solver integration
        nx_solver_file = self.project_root / 'optimization_engine' / 'nx_solver.py'
        if nx_solver_file.exists():
            capabilities['nx_solver'] = True
            content = nx_solver_file.read_text(encoding='utf-8')

            # Check for specific solution types
            if 'sol101' in content.lower() or 'SOL101' in content:
                capabilities['sol101'] = True
            if 'sol103' in content.lower() or 'SOL103' in content:
                capabilities['sol103'] = True
            if 'sol106' in content.lower() or 'SOL106' in content:
                capabilities['sol106'] = True

        # Check for journal execution
        if self._file_contains_pattern(
            self.project_root / 'optimization_engine',
            r'def\s+run.*journal|def\s+execute.*journal'
        ):
            capabilities['journal_execution'] = True

        return capabilities

    def _analyze_result_extraction(self) -> Dict[str, bool]:
        """Analyze result extraction capabilities."""
        capabilities = {
            'displacement': False,
            'stress': False,
            'strain': False,
            'modal': False,
            'temperature': False
        }

        # Check result extractors directory
        extractors_dir = self.project_root / 'optimization_engine' / 'result_extractors'
        if extractors_dir.exists():
            # Look for OP2 extraction capabilities
            for py_file in extractors_dir.glob('*.py'):
                content = py_file.read_text(encoding='utf-8')

                # Check for displacement extraction
                if re.search(r'displacement|displacements', content, re.IGNORECASE):
                    capabilities['displacement'] = True

                # Check for stress extraction
                if re.search(r'stress|von_mises', content, re.IGNORECASE):
                    capabilities['stress'] = True

                # Check for strain extraction
                if re.search(r'strain|strains', content, re.IGNORECASE):
                    # Need to verify it's actual extraction, not just a comment
                    if re.search(r'def\s+\w*extract.*strain|strain.*=.*op2', content, re.IGNORECASE):
                        capabilities['strain'] = True

                # Check for modal extraction
                if re.search(r'modal|mode_shape|eigenvalue', content, re.IGNORECASE):
                    capabilities['modal'] = True

                # Check for temperature extraction
                if re.search(r'temperature|thermal', content, re.IGNORECASE):
                    capabilities['temperature'] = True

        return capabilities

    def _analyze_geometry(self) -> Dict[str, bool]:
        """Analyze geometry-related capabilities."""
        capabilities = {
            'parameter_extraction': False,
            'expression_filtering': False,
            'feature_creation': False
        }

        # Check for parameter extraction (including expression reading/finding)
        if self._file_contains_pattern(
            self.project_root / 'optimization_engine',
            r'def\s+extract.*parameter|def\s+get.*parameter|def\s+find.*expression|def\s+read.*expression|def\s+get.*expression'
        ):
            capabilities['parameter_extraction'] = True

        # Check for expression filtering (v_ prefix)
        if self._file_contains_pattern(
            self.project_root / 'optimization_engine',
            r'v_|filter.*expression|contains.*v_'
        ):
            capabilities['expression_filtering'] = True

        # Check for feature creation
        if self._file_contains_pattern(
            self.project_root / 'optimization_engine',
            r'def\s+create.*feature|def\s+add.*feature'
        ):
            capabilities['feature_creation'] = True

        return capabilities

    def _analyze_materials(self) -> Dict[str, bool]:
        """Analyze material-related capabilities."""
        capabilities = {
            'xml_generation': False,
            'material_assignment': False
        }

        # Check for material XML generation
        material_files = list(self.project_root.glob('optimization_engine/custom_functions/*material*.py'))
        if material_files:
            capabilities['xml_generation'] = True

        # Check for material assignment
        if self._file_contains_pattern(
            self.project_root / 'optimization_engine',
            r'def\s+assign.*material|def\s+set.*material'
        ):
            capabilities['material_assignment'] = True

        return capabilities

    def _file_contains_pattern(self, directory: Path, pattern: str) -> bool:
        """Check if any Python file in directory contains the regex pattern."""
        if not directory.exists():
            return False

        for py_file in directory.rglob('*.py'):
            try:
                content = py_file.read_text(encoding='utf-8')
                if re.search(pattern, content):
                    return True
            except Exception:
                continue

        return False

    def get_capability_details(self, category: str, capability: str) -> Optional[Dict[str, Any]]:
        """Get detailed information about a specific capability."""
        if category not in self.capabilities:
            return None

        if capability not in self.capabilities[category]:
            return None

        if not self.capabilities[category][capability]:
            return None

        # Find the file that implements this capability
        details = {
            'exists': True,
            'category': category,
            'name': capability,
            'implementation_files': []
        }

        # Search for implementation files based on category
        search_patterns = {
            'optimization': ['optuna', 'parameter', 'expression'],
            'simulation': ['nx_solver', 'journal'],
            'result_extraction': ['op2', 'extractor', 'result'],
            'geometry': ['parameter', 'expression', 'geometry'],
            'materials': ['material', 'xml']
        }

        if category in search_patterns:
            for pattern in search_patterns[category]:
                for py_file in (self.project_root / 'optimization_engine').rglob(f'*{pattern}*.py'):
                    if py_file.is_file():
                        details['implementation_files'].append(str(py_file.relative_to(self.project_root)))

        return details

    def find_similar_capabilities(self, missing_capability: str, category: str) -> List[str]:
        """Find existing capabilities similar to the missing one."""
        if category not in self.capabilities:
            return []

        similar = []

        # Special case: for result_extraction, all extraction types are similar
        # because they use the same OP2 extraction pattern
        if category == 'result_extraction':
            for capability, exists in self.capabilities[category].items():
                if exists and capability != missing_capability:
                    similar.append(capability)
            return similar

        # Simple similarity: check if words overlap
        missing_words = set(missing_capability.lower().split('_'))

        for capability, exists in self.capabilities[category].items():
            if not exists:
                continue

            capability_words = set(capability.lower().split('_'))

            # If there's word overlap, consider it similar
            if missing_words & capability_words:
                similar.append(capability)

        return similar

    def get_summary(self) -> str:
        """Get a human-readable summary of capabilities."""
        if not self.capabilities:
            self.analyze_codebase()

        lines = ["Atomizer Codebase Capabilities Summary", "=" * 50, ""]

        for category, caps in self.capabilities.items():
            if not caps:
                continue

            existing = [name for name, exists in caps.items() if exists]
            missing = [name for name, exists in caps.items() if not exists]

            if existing:
                lines.append(f"{category.upper()}:")
                lines.append(f"  Implemented ({len(existing)}):")
                for cap in existing:
                    lines.append(f"    - {cap}")

                if missing:
                    lines.append(f"  Not Found ({len(missing)}):")
                    for cap in missing:
                        lines.append(f"    - {cap}")
                lines.append("")

        return "\n".join(lines)


def main():
    """Test the codebase analyzer."""
    analyzer = CodebaseCapabilityAnalyzer()

    print("Analyzing Atomizer codebase...")
    print("=" * 80)

    capabilities = analyzer.analyze_codebase()

    print("\nCapabilities Found:")
    print("-" * 80)
    print(analyzer.get_summary())

    print("\nDetailed Check: Result Extraction")
    print("-" * 80)
    for capability, exists in capabilities['result_extraction'].items():
        status = "FOUND" if exists else "MISSING"
        print(f"  {capability:20s} : {status}")

        if exists:
            details = analyzer.get_capability_details('result_extraction', capability)
            if details and details.get('implementation_files'):
                print(f"    Files: {', '.join(details['implementation_files'][:2])}")

    print("\nSimilar to 'strain':")
    print("-" * 80)
    similar = analyzer.find_similar_capabilities('strain', 'result_extraction')
    if similar:
        for cap in similar:
            print(f"  - {cap} (could be used as pattern)")
    else:
        print("  No similar capabilities found")


if __name__ == '__main__':
    main()
