#!/usr/bin/env python3
"""
Automated script to migrate all Tailwind default colors to Kodo design system colors.
Uses TAILWIND_INSTANCES_FULL_LIST.md to find all instances and migrate them automatically.
"""

import re
import sys
from pathlib import Path
from typing import Dict, List, Tuple, Optional
from collections import defaultdict

# Color mapping from Tailwind to Kodo design system
COLOR_MAPPINGS = {
    # Gray text colors
    'text-gray-50': 'text-kodo-text-main',
    'text-gray-100': 'text-kodo-text-main',
    'text-gray-200': 'text-kodo-text-main',
    'text-gray-300': 'text-kodo-text-main',
    'text-gray-400': 'text-kodo-content-dim',
    'text-gray-500': 'text-kodo-content-dim',
    'text-gray-600': 'text-kodo-content-dim',
    'text-gray-700': 'text-kodo-text-main',
    'text-gray-800': 'text-kodo-text-main',
    'text-gray-900': 'text-kodo-text-main',
    
    # Gray background colors
    'bg-gray-50': 'bg-kodo-void',
    'bg-gray-100': 'bg-kodo-void',
    'bg-gray-200': 'bg-kodo-slate',
    'bg-gray-300': 'bg-kodo-slate',
    'bg-gray-400': 'bg-kodo-steel',
    'bg-gray-500': 'bg-kodo-steel',
    'bg-gray-600': 'bg-kodo-steel',
    'bg-gray-700': 'bg-kodo-steel',
    'bg-gray-800': 'bg-kodo-graphite',
    'bg-gray-900': 'bg-kodo-ink',
    
    # Gray border colors
    'border-gray-200': 'border-kodo-steel',
    'border-gray-300': 'border-kodo-steel',
    'border-gray-400': 'border-kodo-steel',
    'border-gray-500': 'border-kodo-steel',
    'border-gray-600': 'border-kodo-steel',
    'border-gray-700': 'border-kodo-steel',
    'border-gray-800': 'border-kodo-steel',
    
    # Blue colors (map to cyan when appropriate)
    'text-blue-50': 'text-kodo-cyan',
    'text-blue-100': 'text-kodo-cyan',
    'text-blue-200': 'text-kodo-cyan',
    'text-blue-300': 'text-kodo-cyan',
    'text-blue-400': 'text-kodo-cyan',
    'text-blue-500': 'text-kodo-cyan',
    'text-blue-600': 'text-kodo-cyan',
    'text-blue-700': 'text-kodo-cyan',
    'text-blue-800': 'text-kodo-cyan',
    'text-blue-900': 'text-kodo-cyan',
    
    'bg-blue-50': 'bg-kodo-cyan/10',
    'bg-blue-100': 'bg-kodo-cyan/20',
    'bg-blue-200': 'bg-kodo-cyan/30',
    'bg-blue-300': 'bg-kodo-cyan/40',
    'bg-blue-400': 'bg-kodo-cyan',
    'bg-blue-500': 'bg-kodo-cyan',
    'bg-blue-600': 'bg-kodo-cyan',
    'bg-blue-700': 'bg-kodo-cyan',
    'bg-blue-800': 'bg-kodo-cyan',
    'bg-blue-900': 'bg-kodo-cyan',
    
    'border-blue-200': 'border-kodo-cyan',
    'border-blue-300': 'border-kodo-cyan',
    'border-blue-400': 'border-kodo-cyan',
    'border-blue-500': 'border-kodo-cyan',
    'border-blue-600': 'border-kodo-cyan',
    
    # Red colors (map to kodo-red when appropriate)
    'text-red-400': 'text-kodo-red',
    'text-red-500': 'text-kodo-red',
    'text-red-600': 'text-kodo-red',
    'text-red-700': 'text-kodo-red',
    
    'bg-red-400': 'bg-kodo-red',
    'bg-red-500': 'bg-kodo-red',
    'bg-red-600': 'bg-kodo-red',
    'bg-red-700': 'bg-kodo-red',
    
    'border-red-200': 'border-kodo-red',
    'border-red-300': 'border-kodo-red',
    'border-red-400': 'border-kodo-red',
    'border-red-500': 'border-kodo-red',
    'border-red-600': 'border-kodo-red',
    
    # Green colors (map to kodo-lime when appropriate)
    'text-green-400': 'text-kodo-lime',
    'text-green-500': 'text-kodo-lime',
    'text-green-600': 'text-kodo-lime',
    'text-green-700': 'text-kodo-lime',
    
    'bg-green-50': 'bg-kodo-lime/10',
    'bg-green-100': 'bg-kodo-lime/20',
    'bg-green-200': 'bg-kodo-lime/30',
    'border-green-200': 'border-kodo-lime',
    'border-green-300': 'border-kodo-lime',
    
    # Gradient colors
    'from-gray-700': 'from-kodo-steel',
    'from-gray-800': 'from-kodo-graphite',
    'from-gray-900': 'from-kodo-ink',
    'to-gray-700': 'to-kodo-steel',
    'to-gray-800': 'to-kodo-graphite',
    'to-gray-900': 'to-kodo-ink',
    'to-blue-600': 'to-kodo-cyan',
    'from-blue-600': 'from-kodo-cyan',
}

# Pattern to match Tailwind default colors
TAILWIND_PATTERN = re.compile(
    r'\b(' + '|'.join(re.escape(k) for k in COLOR_MAPPINGS.keys()) + r')\b'
)

def parse_instance_list(list_file: Path) -> Dict[str, List[Tuple[int, str]]]:
    """Parse the TAILWIND_INSTANCES_FULL_LIST.md file."""
    instances = defaultdict(list)
    
    if not list_file.exists():
        print(f"Error: {list_file} not found")
        return instances
    
    current_file = None
    with open(list_file, 'r', encoding='utf-8') as f:
        for line in f:
            # Match file header: ### apps/web/src/...
            file_match = re.match(r'^### (.+)$', line)
            if file_match:
                current_file = file_match.group(1)
                continue
            
            # Match line entry: - **Line 123**: `text-gray-400`
            line_match = re.match(r'^- \*\*Line (\d+)\*\*:', line)
            if line_match and current_file:
                line_num = int(line_match.group(1))
                instances[current_file].append(line_num)
    
    return instances

def migrate_file(file_path: Path, dry_run: bool = False) -> Tuple[int, List[str]]:
    """Migrate a single file, returning count of changes and list of changes."""
    if not file_path.exists():
        return 0, [f"File not found: {file_path}"]
    
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
    except Exception as e:
        return 0, [f"Error reading file: {e}"]
    
    original_content = content
    changes = []
    change_count = 0
    
    # Find all matches and replace them
    def replace_match(match):
        nonlocal change_count
        original = match.group(0)
        if original in COLOR_MAPPINGS:
            replacement = COLOR_MAPPINGS[original]
            if original != replacement:
                change_count += 1
                changes.append(f"  Line {get_line_number(content, match.start())}: {original} → {replacement}")
                return replacement
        return original
    
    # Replace all instances
    new_content = TAILWIND_PATTERN.sub(replace_match, content)
    
    # Also handle cases where colors might be in template strings or conditional expressions
    # This is a more aggressive pass for edge cases
    for original, replacement in COLOR_MAPPINGS.items():
        if original in new_content and original != replacement:
            # Count additional occurrences that might have been missed
            count = new_content.count(original)
            if count > 0:
                new_content = new_content.replace(original, replacement)
                change_count += count
                changes.append(f"  Additional replacements: {original} → {replacement} ({count} times)")
    
    # Write the file if there were changes and not dry run
    if new_content != original_content and not dry_run:
        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(new_content)
        except Exception as e:
            return 0, [f"Error writing file: {e}"]
    
    return change_count, changes

def get_line_number(content: str, position: int) -> int:
    """Get line number from character position."""
    return content[:position].count('\n') + 1

def verify_file(file_path: Path) -> Tuple[bool, List[str]]:
    """Verify that a file has no remaining Tailwind default colors."""
    if not file_path.exists():
        return False, ["File not found"]
    
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
    except Exception as e:
        return False, [f"Error reading file: {e}"]
    
    issues = []
    for line_num, line in enumerate(content.split('\n'), 1):
        # Check for any Tailwind default colors
        matches = TAILWIND_PATTERN.findall(line)
        if matches:
            issues.append(f"Line {line_num}: Still contains {', '.join(matches)}")
    
    return len(issues) == 0, issues

def main():
    """Main execution function."""
    root = Path(__file__).parent.parent
    list_file = root / 'apps' / 'web' / 'docs' / 'TAILWIND_INSTANCES_FULL_LIST.md'
    src_root = root / 'apps' / 'web' / 'src'
    
    # Parse command line arguments
    dry_run = '--dry-run' in sys.argv
    verify_only = '--verify' in sys.argv
    file_limit = None
    if '--limit' in sys.argv:
        idx = sys.argv.index('--limit')
        if idx + 1 < len(sys.argv):
            file_limit = int(sys.argv[idx + 1])
    
    if verify_only:
        print("=== VERIFICATION MODE ===")
        print("Checking files for remaining Tailwind default colors...\n")
    elif dry_run:
        print("=== DRY RUN MODE ===")
        print("Will show changes without modifying files...\n")
    else:
        print("=== MIGRATION MODE ===")
        print("Migrating Tailwind default colors to Kodo design system...\n")
    
    # Parse instance list
    print(f"Reading instance list from: {list_file}")
    instances = parse_instance_list(list_file)
    print(f"Found {len(instances)} files with instances\n")
    
    if file_limit:
        print(f"Limiting to first {file_limit} files\n")
        instances = dict(list(instances.items())[:file_limit])
    
    # Process each file
    total_changes = 0
    files_processed = 0
    files_with_errors = []
    files_verified = []
    files_failed_verification = []
    
    for file_path_str, line_numbers in sorted(instances.items()):
        file_path = root / file_path_str
        
        if not file_path.exists():
            print(f"⚠️  {file_path_str}: File not found")
            files_with_errors.append(file_path_str)
            continue
        
        files_processed += 1
        print(f"[{files_processed}/{len(instances)}] Processing: {file_path_str}")
        print(f"  Expected instances on lines: {', '.join(map(str, line_numbers))}")
        
        if verify_only:
            is_valid, issues = verify_file(file_path)
            if is_valid:
                print(f"  ✅ Verified: No Tailwind default colors found")
                files_verified.append(file_path_str)
            else:
                print(f"  ❌ Failed verification:")
                for issue in issues:
                    print(f"    {issue}")
                files_failed_verification.append(file_path_str)
        else:
            change_count, changes = migrate_file(file_path, dry_run=dry_run)
            total_changes += change_count
            
            if change_count > 0:
                print(f"  ✅ Migrated {change_count} instance(s):")
                for change in changes[:5]:  # Show first 5 changes
                    print(change)
                if len(changes) > 5:
                    print(f"  ... and {len(changes) - 5} more")
            else:
                print(f"  ⚠️  No changes made (may already be migrated)")
            
            # Verify after migration
            if not dry_run:
                is_valid, issues = verify_file(file_path)
                if is_valid:
                    print(f"  ✅ Verified: No remaining Tailwind default colors")
                    files_verified.append(file_path_str)
                else:
                    print(f"  ⚠️  Verification found remaining issues:")
                    for issue in issues[:3]:  # Show first 3 issues
                        print(f"    {issue}")
                    if len(issues) > 3:
                        print(f"    ... and {len(issues) - 3} more")
                    files_failed_verification.append(file_path_str)
        
        print()
    
    # Summary
    print("=" * 60)
    print("SUMMARY")
    print("=" * 60)
    print(f"Files processed: {files_processed}")
    if not verify_only:
        print(f"Total changes made: {total_changes}")
    print(f"Files verified: {len(files_verified)}")
    if files_failed_verification:
        print(f"Files with remaining issues: {len(files_failed_verification)}")
        for f in files_failed_verification[:10]:
            print(f"  - {f}")
        if len(files_failed_verification) > 10:
            print(f"  ... and {len(files_failed_verification) - 10} more")
    if files_with_errors:
        print(f"Files with errors: {len(files_with_errors)}")
        for f in files_with_errors[:5]:
            print(f"  - {f}")
    
    if verify_only:
        if files_failed_verification:
            sys.exit(1)
    elif not dry_run and files_failed_verification:
        print("\n⚠️  Some files still have remaining Tailwind default colors.")
        print("   Please review and fix manually.")
        sys.exit(1)

if __name__ == '__main__':
    main()
