#!/usr/bin/env python3
"""
Cursor Rules Generator
Automates the creation of Cursor rules based on codebase analysis.
Based on PageAI tutorial: https://pageai.pro/blog/cursor-rules-tutorial
"""

import os
import re
import json
from pathlib import Path
from typing import Dict, List, Optional
from dataclasses import dataclass

@dataclass
class RuleTemplate:
    """Template for generating Cursor rules"""
    name: str
    description: str
    globs: str
    always_apply: bool
    content: str

class CursorRulesGenerator:
    """Generate Cursor rules automatically from codebase analysis"""
    
    def __init__(self, project_root: Path):
        self.project_root = project_root
        self.rules_dir = project_root / ".cursor" / "rules"
        self.rules_dir.mkdir(parents=True, exist_ok=True)
    
    def analyze_file_patterns(self, file_paths: List[Path]) -> Dict[str, List[str]]:
        """Analyze files to identify common patterns"""
        patterns = {
            "imports": [],
            "function_definitions": [],
            "class_definitions": [],
            "error_handling": [],
            "testing_patterns": [],
            "naming_conventions": []
        }
        
        for file_path in file_paths:
            if file_path.suffix in ['.py', '.js', '.ts', '.tsx']:
                content = file_path.read_text()
                
                # Extract patterns
                patterns["imports"].extend(self._extract_imports(content))
                patterns["function_definitions"].extend(self._extract_functions(content))
                patterns["class_definitions"].extend(self._extract_classes(content))
                patterns["error_handling"].extend(self._extract_error_handling(content))
                patterns["testing_patterns"].extend(self._extract_testing_patterns(content))
                patterns["naming_conventions"].extend(self._extract_naming_conventions(content))
        
        return patterns
    
    def _extract_imports(self, content: str) -> List[str]:
        """Extract import patterns"""
        import_patterns = []
        lines = content.split('\n')
        
        for line in lines:
            if line.strip().startswith(('import ', 'from ')):
                import_patterns.append(line.strip())
        
        return import_patterns[:10]  # Limit to first 10
    
    def _extract_functions(self, content: str) -> List[str]:
        """Extract function definition patterns"""
        function_patterns = []
        
        # Python functions
        python_funcs = re.findall(r'def\s+(\w+)\s*\([^)]*\)\s*:', content)
        function_patterns.extend(python_funcs)
        
        # JavaScript/TypeScript functions
        js_funcs = re.findall(r'(?:function\s+)?(\w+)\s*\([^)]*\)\s*{', content)
        function_patterns.extend(js_funcs)
        
        return function_patterns[:10]
    
    def _extract_classes(self, content: str) -> List[str]:
        """Extract class definition patterns"""
        class_patterns = []
        
        # Python classes
        python_classes = re.findall(r'class\s+(\w+)', content)
        class_patterns.extend(python_classes)
        
        # JavaScript/TypeScript classes
        js_classes = re.findall(r'class\s+(\w+)', content)
        class_patterns.extend(js_classes)
        
        return class_patterns[:10]
    
    def _extract_error_handling(self, content: str) -> List[str]:
        """Extract error handling patterns"""
        error_patterns = []
        
        # Python try/except
        try_except = re.findall(r'try:\s*\n(.*?)\nexcept', content, re.DOTALL)
        error_patterns.extend(try_except)
        
        # JavaScript try/catch
        try_catch = re.findall(r'try\s*{\s*\n(.*?)\n}\s*catch', content, re.DOTALL)
        error_patterns.extend(try_catch)
        
        return error_patterns[:5]
    
    def _extract_testing_patterns(self, content: str) -> List[str]:
        """Extract testing patterns"""
        test_patterns = []
        
        # Python pytest
        pytest_funcs = re.findall(r'def\s+test_\w+', content)
        test_patterns.extend(pytest_funcs)
        
        # JavaScript/TypeScript tests
        js_tests = re.findall(r'(?:it|test|describe)\s*\(', content)
        test_patterns.extend(js_tests)
        
        return test_patterns[:5]
    
    def _extract_naming_conventions(self, content: str) -> List[str]:
        """Extract naming convention patterns"""
        naming_patterns = []
        
        # Variable names
        variables = re.findall(r'(\w+)\s*=', content)
        naming_patterns.extend(variables[:10])
        
        # Function names
        functions = re.findall(r'def\s+(\w+)', content)
        naming_patterns.extend(functions[:10])
        
        return naming_patterns
    
    def generate_rule_content(self, patterns: Dict[str, List[str]], rule_type: str) -> str:
        """Generate rule content based on patterns"""
        
        if rule_type == "python":
            return self._generate_python_rule(patterns)
        elif rule_type == "javascript":
            return self._generate_javascript_rule(patterns)
        elif rule_type == "testing":
            return self._generate_testing_rule(patterns)
        else:
            return self._generate_generic_rule(patterns)
    
    def _generate_python_rule(self, patterns: Dict[str, List[str]]) -> str:
        """Generate Python-specific rule"""
        content = """# Python Development Rules

## Import Patterns

Based on your codebase, use these import patterns:

```python
# Standard library imports first
import os
import re
from pathlib import Path
from typing import Dict, List, Optional

# Third-party imports
import click
from rich.console import Console

# Local imports
from src.config import config
from src.services.protocols import TranscriptionServiceProtocol
```

## Function Definitions

Follow these patterns for function definitions:

```python
def function_name(param1: str, param2: Optional[int] = None) -> ReturnType:
    \"\"\"Docstring describing the function's purpose.\"\"\"
    # Implementation
    return result
```

## Error Handling

Use consistent error handling patterns:

```python
try:
    # Operation that might fail
    result = process_data(input_data)
except SpecificError as e:
    logger.error(f"Failed to process data: {e}")
    raise
except Exception as e:
    logger.error(f"Unexpected error: {e}")
    raise
```

## Naming Conventions

- Use `snake_case` for functions and variables
- Use `PascalCase` for classes
- Use `UPPER_CASE` for constants
- Use descriptive names that explain purpose
"""
        return content
    
    def _generate_javascript_rule(self, patterns: Dict[str, List[str]]) -> str:
        """Generate JavaScript-specific rule"""
        content = """# JavaScript/TypeScript Development Rules

## Import Patterns

```typescript
// Third-party imports first
import React from 'react';
import { useState, useEffect } from 'react';

// Local imports
import { ComponentName } from './ComponentName';
import { useCustomHook } from '../hooks/useCustomHook';
```

## Function Definitions

```typescript
// Function declarations
function functionName(param1: string, param2?: number): ReturnType {
  // Implementation
  return result;
}

// Arrow functions for callbacks
const handleClick = (event: React.MouseEvent): void => {
  // Implementation
};
```

## Error Handling

```typescript
try {
  const result = await apiCall();
  return result;
} catch (error) {
  console.error('API call failed:', error);
  throw error;
}
```

## Naming Conventions

- Use `camelCase` for functions and variables
- Use `PascalCase` for components and classes
- Use `UPPER_CASE` for constants
- Use descriptive names that explain purpose
"""
        return content
    
    def _generate_testing_rule(self, patterns: Dict[str, List[str]]) -> str:
        """Generate testing-specific rule"""
        content = """# Testing Rules

## Test Structure

```python
# Python (pytest)
def test_function_name():
    \"\"\"Test description.\"\"\"
    # Arrange
    input_data = "test input"
    
    # Act
    result = function_to_test(input_data)
    
    # Assert
    assert result == expected_output
```

```typescript
// JavaScript/TypeScript (Jest)
describe('ComponentName', () => {
  it('should render correctly', () => {
    // Arrange
    const props = { test: 'value' };
    
    // Act
    render(<ComponentName {...props} />);
    
    // Assert
    expect(screen.getByText('expected text')).toBeInTheDocument();
  });
});
```

## Testing Best Practices

- Write tests for both success and failure cases
- Use descriptive test names that explain the scenario
- Follow AAA pattern (Arrange, Act, Assert)
- Mock external dependencies
- Test edge cases and error conditions
"""
        return content
    
    def _generate_generic_rule(self, patterns: Dict[str, List[str]]) -> str:
        """Generate generic rule"""
        content = """# Generic Development Rules

## Code Organization

- Keep functions small and focused
- Use meaningful variable names
- Add comments for complex logic
- Follow consistent formatting

## Error Handling

- Always handle potential errors
- Provide meaningful error messages
- Log errors appropriately
- Don't ignore exceptions

## Performance

- Optimize for readability first
- Profile before optimizing
- Use appropriate data structures
- Avoid premature optimization
"""
        return content
    
    def create_rule_file(self, rule_name: str, content: str, description: str = "", 
                        globs: str = "**/*", always_apply: bool = False) -> Path:
        """Create a new Cursor rule file"""
        
        rule_path = self.rules_dir / f"{rule_name}.mdc"
        
        frontmatter = f"""---
description: {description or f"Rules for {rule_name}"}
globs: {globs}
alwaysApply: {str(always_apply).lower()}
---

"""
        
        full_content = frontmatter + content
        rule_path.write_text(full_content)
        
        print(f"✅ Created rule: {rule_path}")
        return rule_path
    
    def generate_rules_from_directory(self, source_dir: str, rule_type: str = "generic") -> List[Path]:
        """Generate rules from a specific directory"""
        source_path = self.project_root / source_dir
        
        if not source_path.exists():
            print(f"❌ Directory not found: {source_path}")
            return []
        
        # Find relevant files
        file_extensions = {
            "python": [".py"],
            "javascript": [".js", ".ts", ".tsx"],
            "testing": [".py", ".js", ".ts", ".tsx"]
        }
        
        extensions = file_extensions.get(rule_type, [".py", ".js", ".ts", ".tsx"])
        files = []
        
        for ext in extensions:
            files.extend(source_path.rglob(f"*{ext}"))
        
        if not files:
            print(f"❌ No files found in {source_dir}")
            return []
        
        # Analyze patterns
        patterns = self.analyze_file_patterns(files)
        
        # Generate rule content
        content = self.generate_rule_content(patterns, rule_type)
        
        # Create rule file
        rule_name = f"{source_dir.replace('/', '-')}-patterns"
        rule_path = self.create_rule_file(
            rule_name=rule_name,
            content=content,
            description=f"Patterns and conventions for {source_dir}",
            globs=f"{source_dir}/**/*",
            always_apply=False
        )
        
        return [rule_path]

def main():
    """Main function to run the rule generator"""
    project_root = Path.cwd()
    generator = CursorRulesGenerator(project_root)
    
    # Example usage
    print("🔧 Cursor Rules Generator")
    print("=" * 50)
    
    # Generate rules for different directories
    directories = [
        ("src", "python"),
        ("tests", "testing"),
        ("scripts", "python")
    ]
    
    created_rules = []
    for directory, rule_type in directories:
        print(f"\n📁 Analyzing {directory}...")
        rules = generator.generate_rules_from_directory(directory, rule_type)
        created_rules.extend(rules)
    
    print(f"\n✅ Generated {len(created_rules)} rules:")
    for rule in created_rules:
        print(f"  - {rule.name}")

if __name__ == "__main__":
    main()
