#!/usr/bin/env python3
"""
Large-scale Android project brand rename.
Handles content replacement, package dir rename, file rename, Room schema fix.

Usage: python3 rename_project.py <old_brand> <new_brand> [--dry-run]
Example: python3 rename_project.py Mr.VHS Unspooled

This script performs:
1. Content replacement across all .kt, .xml, .kts, .java, .json, .properties files
2. Package directory rename (com.oldbrand → com.newbrand)
3. File renaming for .kt files that were renamed in content
4. Room schema directory + content fix
5. .idea/workspace.xml fix
6. Final verification sweep

DRY RUN: adds --dry-run to preview changes without modifying files.
"""

import os
import sys
import re
import shutil
from pathlib import Path

def find_files(root, extensions, exclude_dirs=None):
    """Find all files with given extensions under root."""
    exclude = exclude_dirs or {'.git', '.gradle', 'build', 'app/build', '.idea', '.kotlin'}
    results = []
    for dirpath, dirnames, filenames in os.walk(root):
        # Skip excluded directories
        dirnames[:] = [d for d in dirnames if d not in exclude]
        for fname in filenames:
            if any(fname.endswith(ext) for ext in extensions):
                results.append(os.path.join(dirpath, fname))
    return results

def replace_in_file(filepath, replacements, dry_run=False):
    """Apply ordered replacements to a file. Returns True if any change was made."""
    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            content = f.read()
    except (UnicodeDecodeError, PermissionError):
        return False

    original = content
    for old, new in replacements:
        content = content.replace(old, new)

    if content != original:
        if not dry_run:
            with open(filepath, 'w', encoding='utf-8') as f:
                f.write(content)
        return True
    return False

def rename_package_dir(root, old_pkg, new_pkg):
    """Rename com.oldbrand → com.newbrand directories."""
    old_path = os.path.join(root, 'src', 'main', 'java', *old_pkg.split('.'))
    new_path = os.path.join(root, 'src', 'main', 'java', *new_pkg.split('.'))
    if os.path.exists(old_path):
        if not os.path.exists(os.path.dirname(new_path)):
            os.makedirs(os.path.dirname(new_path))
        shutil.move(old_path, new_path)
        print(f"  Renamed: {old_path} → {new_path}")
        return True
    return False

def rename_test_package_dir(root, old_pkg, new_pkg):
    """Rename com.oldbrand → com.newbrand test directories."""
    old_path = os.path.join(root, 'src', 'test', 'java', *old_pkg.split('.'))
    new_path = os.path.join(root, 'src', 'test', 'java', *new_pkg.split('.'))
    if os.path.exists(old_path):
        if not os.path.exists(os.path.dirname(new_path)):
            os.makedirs(os.path.dirname(new_path))
        shutil.move(old_path, new_path)
        print(f"  Renamed: {old_path} → {new_path}")
        return True
    return False

def rename_kt_files(root, old_name, new_name):
    """Rename .kt files that contain the old class name in their filename."""
    renamed = []
    for dirpath, _, filenames in os.walk(root):
        for fname in filenames:
            if fname.endswith('.kt') and old_name in fname:
                old = os.path.join(dirpath, fname)
                new = os.path.join(dirpath, fname.replace(old_name, new_name))
                if not os.path.exists(new):
                    if not os.path.exists(os.path.dirname(new)):
                        os.makedirs(os.path.dirname(new))
                    shutil.move(old, new)
                    renamed.append((old, new))
                    print(f"  Renamed file: {old} → {new}")
    return renamed

def fix_room_schemas(root, old_class, new_class):
    """Fix Room schema directory name and content."""
    old_schema_dir = os.path.join(root, 'schemas', old_class)
    new_schema_dir = os.path.join(root, 'schemas', new_class)
    if os.path.exists(old_schema_dir):
        if not os.path.exists(os.path.dirname(new_schema_dir)):
            os.makedirs(os.path.dirname(new_schema_dir))
        shutil.move(old_schema_dir, new_schema_dir)
        print(f"  Renamed schema dir: {old_schema_dir} → {new_schema_dir}")
        # Fix content inside JSON files
        replacements = [
            (old_class, new_class),
            (old_class.replace('.', '/'), new_class.replace('.', '/')),
        ]
        for json_file in find_files(new_schema_dir, ['.json']):
            replace_in_file(json_file, replacements)
        return True
    return False

def fix_idea_files(root, old_brand, new_brand):
    """Fix .idea/workspace.xml and other .idea files."""
    idea_dir = os.path.join(root, '.idea')
    if not os.path.exists(idea_dir):
        return
    replacements = [
        (old_brand, new_brand),
        (old_brand.lower(), new_brand.lower()),
    ]
    for dirpath, _, filenames in os.walk(idea_dir):
        for fname in filenames:
            if fname.endswith('.xml') or fname.endswith('.properties'):
                filepath = os.path.join(dirpath, fname)
                replace_in_file(filepath, replacements)

def verify_sweep(root, old_brand, new_brand):
    """Final sweep — should return 0 matches."""
    extensions = ['.kt', '.xml', '.kts', '.java', '.json', '.properties', '.gradle']
    files = find_files(root, extensions)
    matches = 0
    for fpath in files:
        try:
            with open(fpath, 'r', encoding='utf-8') as f:
                content = f.read()
            if old_brand in content or old_brand.lower() in content:
                matches += 1
                print(f"  REMAINING: {fpath}")
        except (UnicodeDecodeError, PermissionError):
            pass
    return matches

def main():
    if len(sys.argv) < 3:
        print("Usage: python3 rename_project.py <OldBrand> <NewBrand> [--dry-run]")
        sys.exit(1)

    old_brand = sys.argv[1]
    new_brand = sys.argv[2]
    dry_run = '--dry-run' in sys.argv

    # Determine project root (look for app/build.gradle.kts)
    root = os.getcwd()
    gradle_file = os.path.join(root, 'app', 'build.gradle.kts')
    if not os.path.exists(gradle_file):
        # Walk up to find it
        for parent in [root] + list(Path(root).parents):
            candidate = os.path.join(parent, 'app', 'build.gradle.kts')
            if os.path.exists(candidate):
                root = parent
                break

    print(f"Project root: {root}")
    print(f"Renaming: {old_brand} → {new_brand}")
    print(f"Dry run: {dry_run}")
    print()

    # Build replacement list (longer/more-specific first)
    replacements = [
        (f'com.{old_brand.lower()}.app', f'com.{new_brand.lower()}.app'),
        (f'com.{old_brand.lower()}', f'com.{new_brand.lower()}'),
        (old_brand, new_brand),
        (old_brand.lower(), new_brand.lower()),
        (old_brand.upper(), new_brand.upper()),
    ]

    # Step 1: Content replacement
    print("=== Step 1: Content replacement ===")
    extensions = ['.kt', '.xml', '.kts', '.java', '.json', '.properties']
    files = find_files(os.path.join(root, 'app', 'src'), extensions)
    changed = 0
    for fpath in files:
        if replace_in_file(fpath, replacements, dry_run):
            changed += 1
    print(f"  Changed {changed} files")

    # Step 2: Package directory rename
    print("\n=== Step 2: Package directory rename ===")
    old_pkg = old_brand.lower()
    new_pkg = new_brand.lower()
    rename_package_dir(os.path.join(root, 'app', 'src', 'main'), old_pkg, new_pkg)
    rename_test_package_dir(os.path.join(root, 'app', 'src', 'test'), old_pkg, new_pkg)

    # Step 3: File rename
    print("\n=== Step 3: File rename ===")
    rename_kt_files(os.path.join(root, 'app', 'src', 'main', 'java', *new_pkg.split('.')), old_brand, new_brand)
    rename_kt_files(os.path.join(root, 'app', 'src', 'test', 'java', *new_pkg.split('.')), old_brand, new_brand)

    # Step 4: Room schemas
    print("\n=== Step 4: Room schema fix ===")
    fix_room_schemas(os.path.join(root, 'app'), f'com.{old_pkg}.data.local.{old_brand}Database',
                     f'com.{new_pkg}.data.local.{new_brand}Database')

    # Step 5: .idea files
    print("\n=== Step 5: .idea fix ===")
    fix_idea_files(root, old_brand, new_brand)

    # Step 6: Verify
    print("\n=== Step 6: Verification sweep ===")
    remaining = verify_sweep(os.path.join(root, 'app', 'src'), old_brand, new_brand)
    print(f"  Remaining matches: {remaining}")

    if dry_run:
        print("\n[DRY RUN — no files were modified]")

if __name__ == '__main__':
    main()
