#!/usr/bin/env python3
"""
Reorganizes a base directory by converting directories with the
Unicode 'fraction slash' character (U+2044 \u2044) into real subdirectory structures.
Works recursively and handles multi-level fake slashes.

Usage:
  python3 reorganize_fakeslash.py <base_dir> [--dry-run]
"""

import os
import sys
import shutil

FAKE_SLASH = '\u2044'
DRY_RUN = '--dry-run' in sys.argv
BASE = sys.argv[1] if len(sys.argv) > 1 and not sys.argv[1].startswith('--') else '/mnt/18tb/downloads/roms'

def log(msg):
    print(msg, flush=True)

def get_all_fake_entries(directory):
    """Get all entries in directory that contain the fake slash character."""
    try:
        return [e for e in os.listdir(directory) if FAKE_SLASH in e]
    except PermissionError:
        return []

def resolve_move(base_dir, entry):
    """
    Given a fake-slash entry name, compute (src_path, dst_path).
    Splits on fake slash to build the real path.
    """
    parts = entry.split(FAKE_SLASH)
    src_path = os.path.join(base_dir, entry)
    dst_path = os.path.join(base_dir, *parts)
    return src_path, dst_path

def collect_all_moves(directory):
    """
    Walk the directory tree and collect all (src, dst) moves needed.
    Processes deeper levels first (leaves before parents) to avoid path conflicts.
    """
    all_moves = []
    
    # BFS to find all directories, then process
    dirs_to_scan = [directory]
    
    while dirs_to_scan:
        current = dirs_to_scan.pop(0)
        
        try:
            entries = sorted(os.listdir(current))
        except (PermissionError, NotADirectoryError, FileNotFoundError):
            continue
        
        fake_entries = [e for e in entries if FAKE_SLASH in e]
        real_entries = [e for e in entries if FAKE_SLASH not in e]
        
        # Add real subdirs to scan queue
        for e in real_entries:
            full = os.path.join(current, e)
            if os.path.isdir(full):
                dirs_to_scan.append(full)
        
        # For fake entries, we need to handle them deepest first
        # Sort by number of fake slashes descending (deepest first)
        fake_entries.sort(key=lambda x: x.count(FAKE_SLASH), reverse=True)
        
        for entry in fake_entries:
            src, dst = resolve_move(current, entry)
            all_moves.append((src, dst, current))
            
            # After move, the entry will be at dst - scan the parent for further nesting
            # We don't scan the entry itself here since it will be moved
    
    return all_moves

log(f"{'[DRY RUN] ' if DRY_RUN else ''}Scanning: {BASE}")
log("")

moves = collect_all_moves(BASE)

log(f"Found {len(moves)} fake-slash entries to reorganize.")
log("")

if DRY_RUN:
    log("=== REORGANIZATION PLAN (sample) ===")
    for src, dst, parent in moves[:30]:
        src_rel = src.replace(BASE, '')
        dst_rel = dst.replace(BASE, '')
        log(f"  MOVE: {src_rel}")
        log(f"    TO: {dst_rel}")
    if len(moves) > 30:
        log(f"  ... and {len(moves) - 30} more")
    log(f"\n[DRY RUN] No changes made.")
    sys.exit(0)

log("=== EXECUTING ===")
errors = []
done = 0

# Process in passes - deepest fake slash counts first, then repeat until no more
pass_num = 0
while True:
    pass_num += 1
    
    try:
        entries_in_base = []
        for root, dirs, files in os.walk(BASE):
            for d in dirs:
                if FAKE_SLASH in d:
                    entries_in_base.append((os.path.join(root, d), root))
    except Exception as e:
        log(f"Error scanning: {e}")
        break
    
    if not entries_in_base:
        break
    
    # Sort: deepest fake slash count first, then longest path first (leaves before parents)
    entries_in_base.sort(key=lambda x: (-x[0].count(FAKE_SLASH), -len(x[0])))
    
    log(f"\n[Pass {pass_num}] Processing {len(entries_in_base)} fake-slash entries...")
    
    pass_errors = 0
    pass_done = 0
    
    for src_path, parent_dir in entries_in_base:
        entry_name = os.path.basename(src_path)
        parts = entry_name.split(FAKE_SLASH)
        dst_path = os.path.join(parent_dir, *parts)
        
        try:
            os.makedirs(os.path.dirname(dst_path), exist_ok=True)
            shutil.move(src_path, dst_path)
            pass_done += 1
            done += 1
        except Exception as e:
            pass_errors += 1
            errors.append((src_path, dst_path, str(e)))
            log(f"  ERROR: {entry_name}: {e}")
    
    log(f"  Done: {pass_done}, Errors: {pass_errors}")
    
    if pass_errors == len(entries_in_base):
        # All failed, avoid infinite loop
        break

log("")
if errors:
    log(f"=== COMPLETED WITH {len(errors)} ERRORS (total moved: {done}) ===")
    for src, dst, err in errors[:10]:
        log(f"  {os.path.basename(src)}: {err}")
else:
    log(f"=== COMPLETED SUCCESSFULLY ===")
    log(f"Reorganized {done} fake-slash directories.")
