#!/usr/bin/env python3
"""
Automated batch migration script for Tailwind default colors.
Processes files in batches, commits after each batch, and verifies all changes.
"""

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

# Import color mappings from the main script
# (In a real scenario, we'd import from the main script, but for simplicity, we'll duplicate)
COLOR_MAPPINGS = {
    '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',
    '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',
    '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',
    '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',
    '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',
    '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',
    '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',
}

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[int]]:
    """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:
            file_match = re.match(r'^### (.+)$', line)
            if file_match:
                current_file = file_match.group(1)
                continue
            
            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."""
    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
    
    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
                line_num = content[:match.start()].count('\n') + 1
                changes.append(f"Line {line_num}: {original} → {replacement}")
                return replacement
        return original
    
    new_content = TAILWIND_PATTERN.sub(replace_match, content)
    
    # Additional pass for any missed instances
    for original, replacement in COLOR_MAPPINGS.items():
        if original in new_content and original != replacement:
            count = new_content.count(original)
            if count > 0:
                new_content = new_content.replace(original, replacement)
                change_count += count
    
    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 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):
        matches = TAILWIND_PATTERN.findall(line)
        if matches:
            issues.append(f"Line {line_num}: Still contains {', '.join(matches)}")
    
    return len(issues) == 0, issues

def git_commit(files: List[str], batch_num: int, total_changes: int) -> bool:
    """Commit the changes."""
    try:
        # Add files
        subprocess.run(['git', 'add'] + files, check=True, capture_output=True)
        
        # Commit
        commit_msg = f"consistency: auto-migrate Tailwind default colors (Batch {batch_num}, {total_changes} instances)"
        result = subprocess.run(
            ['git', 'commit', '-m', commit_msg],
            check=True,
            capture_output=True,
            text=True
        )
        return True
    except subprocess.CalledProcessError as e:
        print(f"  ⚠️  Git commit failed: {e.stderr.decode() if e.stderr else str(e)}")
        return False

def main():
    """Main execution function."""
    root = Path(__file__).parent.parent
    list_file = root / 'apps' / 'web' / 'docs' / 'TAILWIND_INSTANCES_FULL_LIST.md'
    
    batch_size = 10
    if '--batch-size' in sys.argv:
        idx = sys.argv.index('--batch-size')
        if idx + 1 < len(sys.argv):
            batch_size = int(sys.argv[idx + 1])
    
    dry_run = '--dry-run' in sys.argv
    
    print("=== AUTOMATED BATCH MIGRATION ===")
    if dry_run:
        print("DRY RUN MODE - No files will be modified\n")
    else:
        print(f"Processing files in batches of {batch_size}\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")
    
    # Process in batches
    all_files = sorted(instances.keys())
    total_files = len(all_files)
    batch_num = 0
    total_changes_all = 0
    files_verified = []
    files_failed = []
    
    for i in range(0, total_files, batch_size):
        batch_num += 1
        batch_files = all_files[i:i + batch_size]
        
        print(f"\n{'='*60}")
        print(f"BATCH {batch_num} ({len(batch_files)} files)")
        print(f"{'='*60}\n")
        
        batch_changes = 0
        batch_file_paths = []
        
        for file_path_str in batch_files:
            file_path = root / file_path_str
            
            if not file_path.exists():
                print(f"⚠️  {file_path_str}: File not found")
                continue
            
            print(f"  Processing: {file_path_str}")
            change_count, changes = migrate_file(file_path, dry_run=dry_run)
            batch_changes += change_count
            batch_file_paths.append(file_path_str)
            
            if change_count > 0:
                print(f"    ✅ Migrated {change_count} instance(s)")
            else:
                print(f"    ⚠️  No changes (may already be migrated)")
            
            # Verify
            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 issues: {len(issues)} remaining")
                files_failed.append((file_path_str, issues))
        
        total_changes_all += batch_changes
        
        print(f"\n  Batch {batch_num} Summary:")
        print(f"    Files processed: {len(batch_file_paths)}")
        print(f"    Total changes: {batch_changes}")
        print(f"    Files verified: {len([f for f in batch_file_paths if f in files_verified])}")
        
        # Commit batch
        if not dry_run and batch_changes > 0:
            print(f"\n  Committing batch {batch_num}...")
            if git_commit(batch_file_paths, batch_num, batch_changes):
                print(f"  ✅ Committed batch {batch_num}")
            else:
                print(f"  ⚠️  Failed to commit batch {batch_num}")
    
    # Final summary
    print(f"\n{'='*60}")
    print("FINAL SUMMARY")
    print(f"{'='*60}")
    print(f"Total batches: {batch_num}")
    print(f"Total files processed: {total_files}")
    print(f"Total changes made: {total_changes_all}")
    print(f"Files verified: {len(files_verified)}")
    print(f"Files with issues: {len(files_failed)}")
    
    if files_failed:
        print(f"\nFiles that need manual review:")
        for file_path, issues in files_failed[:10]:
            print(f"  - {file_path} ({len(issues)} issues)")
        if len(files_failed) > 10:
            print(f"  ... and {len(files_failed) - 10} more")

if __name__ == '__main__':
    main()
