#!/usr/bin/env python
"""
RichChoiceField Enforcement Lint Script

This script checks for prohibited choice patterns in the codebase.
Exit code 0 = no violations found, exit code 1 = violations found.

Usage:
    python scripts/lint_choices.py
    python scripts/lint_choices.py --fix  # Show fix suggestions
    
Add to CI:
    python scripts/lint_choices.py || exit 1
"""

import argparse
import re
import sys
from pathlib import Path

# Patterns to detect prohibited choice usage
PROHIBITED_PATTERNS = [
    # TextChoices / IntegerChoices class definitions
    (r'class\s+\w+\s*\(\s*models\.(TextChoices|IntegerChoices)\s*\)', 
     'models.TextChoices/IntegerChoices class definition'),
    
    # Inline tuple choices in CharField
    (r'choices\s*=\s*\[\s*\(\s*["\']', 
     'Inline tuple choices'),
    
    # Direct reference to .choices attribute
    (r'choices\s*=\s*\w+\.choices', 
     'Reference to inner TextChoices.choices'),
]

# Directories/files to exclude
EXCLUDE_PATTERNS = [
    '*/migrations/*',
    '*/.venv/*',
    '*/node_modules/*',
    '*/__pycache__/*',
    '*.pyc',
    'lint_choices.py',  # Exclude this script
]

# Files allowed to define TextChoices (infrastructure only)
ALLOWED_EXCEPTION_FILES = [
    # Core choice infrastructure files can reference these patterns
    'apps/core/choices/',
]


def should_exclude(path: Path) -> bool:
    """Check if path should be excluded from linting."""
    path_str = str(path)
    for pattern in EXCLUDE_PATTERNS:
        if pattern.startswith('*/'):
            if pattern[2:].rstrip('/*') in path_str:
                return True
        elif pattern.endswith('/*'):
            if path_str.startswith(pattern[:-2]):
                return True
        elif pattern in path_str:
            return True
    return False


def is_exception_file(path: Path) -> bool:
    """Check if file is an allowed exception."""
    path_str = str(path)
    for exception in ALLOWED_EXCEPTION_FILES:
        if exception in path_str:
            return True
    return False


def scan_file(filepath: Path) -> list[tuple[int, str, str]]:
    """Scan a file for prohibited patterns. Returns list of (line_num, line, pattern_name)."""
    violations = []
    
    if should_exclude(filepath) or is_exception_file(filepath):
        return violations
    
    try:
        content = filepath.read_text(encoding='utf-8')
        lines = content.split('\n')
        
        for i, line in enumerate(lines, 1):
            # Skip comments
            stripped = line.strip()
            if stripped.startswith('#'):
                continue
                
            for pattern, description in PROHIBITED_PATTERNS:
                if re.search(pattern, line):
                    violations.append((i, line.strip(), description))
                    break  # Only report one violation per line
                    
    except Exception as e:
        print(f"Warning: Could not read {filepath}: {e}", file=sys.stderr)
    
    return violations


def scan_directory(root_dir: Path) -> dict[Path, list]:
    """Scan all Python files in directory for violations."""
    all_violations = {}
    
    for filepath in root_dir.rglob('*.py'):
        violations = scan_file(filepath)
        if violations:
            all_violations[filepath] = violations
    
    return all_violations


def print_violations(violations: dict, show_fix: bool = False):
    """Print violations in a readable format."""
    total = 0
    
    for filepath, file_violations in sorted(violations.items()):
        print(f"\n\033[1;31m{filepath}\033[0m")
        for line_num, line_content, description in file_violations:
            print(f"  Line {line_num}: [{description}]")
            print(f"    {line_content[:80]}{'...' if len(line_content) > 80 else ''}")
            total += 1
            
            if show_fix:
                print(f"    \033[1;33mFix:\033[0m Use RichChoiceField(choice_group='...', domain='...')")
    
    return total


def main():
    parser = argparse.ArgumentParser(description='Lint for prohibited choice patterns')
    parser.add_argument('--fix', action='store_true', help='Show fix suggestions')
    parser.add_argument('path', nargs='?', default='apps', help='Path to scan (default: apps)')
    args = parser.parse_args()
    
    # Find backend directory
    script_dir = Path(__file__).parent
    backend_dir = script_dir.parent
    if (backend_dir / 'apps').exists():
        scan_path = backend_dir / args.path
    elif (backend_dir / 'backend' / 'apps').exists():
        scan_path = backend_dir / 'backend' / args.path
    else:
        print("Error: Could not find apps directory", file=sys.stderr)
        sys.exit(1)
    
    print(f"Scanning {scan_path} for prohibited choice patterns...")
    print("=" * 60)
    
    violations = scan_directory(scan_path)
    
    if violations:
        total = print_violations(violations, show_fix=args.fix)
        print("\n" + "=" * 60)
        print(f"\033[1;31mFound {total} violation(s) in {len(violations)} file(s)\033[0m")
        print("\nProhibited patterns:")
        print("  - models.TextChoices / models.IntegerChoices classes")
        print("  - Inline choices=[(value, label), ...]")
        print("  - References to InnerClass.choices")
        print("\nRequired pattern:")
        print("  RichChoiceField(choice_group='group_name', domain='domain_name')")
        print("\nSee: .agent/workflows/enforce-richchoice.md for migration guide")
        sys.exit(1)
    else:
        print("\n\033[1;32m✓ No prohibited choice patterns found!\033[0m")
        sys.exit(0)


if __name__ == '__main__':
    main()
