"""
Blender script to remap texture paths after texture organization.
Usage: blender --background --factory-startup --python remap_texture_paths.py -- [blend_file_path] [texture_moves_json_path]
"""

import bpy
import json
import os
import sys

def normalize_path(path):
    """Normalize a path for comparison (handle case, separators, etc.)"""
    if not path:
        return ""
    # Convert to absolute path and normalize
    abs_path = os.path.abspath(path)
    # Normalize separators (Windows uses backslash, but we'll compare case-insensitively)
    normalized = os.path.normpath(abs_path).replace('\\', '/')
    return normalized.lower()

def load_move_log(json_path):
    """Load the move log JSON file and build lookup dictionaries"""
    try:
        with open(json_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        # Debug: Check what keys are in the data
        print(f"  JSON keys: {list(data.keys())}")
        
        # PowerShell ConvertTo-Json uses PascalCase by default, so check both cases
        moves = data.get('moves', []) or data.get('Moves', [])
        
        print(f"  Found {len(moves)} move entries in JSON")
        
        if len(moves) > 0:
            # Debug: Show first move entry structure
            print(f"  First move entry keys: {list(moves[0].keys())}")
            print(f"  First move entry: {moves[0]}")
        
        # Build lookup dictionaries: normalized original path -> actual new path
        original_to_new = {}
        original_to_replacement = {}
        
        for move in moves:
            # Try both camelCase and PascalCase property names
            move_type = move.get('type', '') or move.get('Type', '')
            original = move.get('originalPath', '') or move.get('OriginalPath', '')
            
            if not original:
                continue
            
            orig_norm = normalize_path(original)
            
            if move_type == 'moved' or move_type == 'Moved':
                new_path = move.get('newPath', '') or move.get('NewPath', '')
                if new_path:
                    original_to_new[orig_norm] = new_path
            
            elif move_type == 'deleted' or move_type == 'Deleted':
                replacement = move.get('replacedBy', '') or move.get('ReplacedBy', '')
                if replacement:
                    original_to_replacement[orig_norm] = replacement
        
        print(f"  Built {len(original_to_new)} move mappings and {len(original_to_replacement)} replacement mappings")
        
        return original_to_new, original_to_replacement, data.get('textureFolderPath', '') or data.get('TextureFolderPath', '')
    
    except Exception as e:
        print(f"ERROR: Failed to load move log: {e}")
        import traceback
        traceback.print_exc()
        return {}, {}, ""

def remap_texture_paths(blend_file_path, move_log_path):
    """Remap all texture paths in the blend file"""
    
    print(f"\n=== REMAPPING TEXTURE PATHS ===")
    print(f"Blend file: {blend_file_path}")
    print(f"Move log: {move_log_path}")
    
    # Load move log
    original_to_new, original_to_replacement, texture_folder_path = load_move_log(move_log_path)
    
    if not original_to_new and not original_to_replacement:
        print("WARNING: No moves found in move log. Nothing to remap.")
        return 0
    
    print(f"Loaded {len(original_to_new)} move mappings and {len(original_to_replacement)} replacement mappings")
    
    # Get blend file directory for relative path conversion
    blend_file_dir = os.path.dirname(os.path.abspath(blend_file_path))
    
    remapped_count = 0
    not_found_count = 0
    
    # Remap paths in image datablocks
    print("\nRemapping image datablock paths...")
    for image in bpy.data.images:
        if not image.filepath:
            continue
        
        # Convert to absolute path for comparison
        abs_path = bpy.path.abspath(image.filepath)
        if not abs_path:
            continue
        
        abs_norm = normalize_path(abs_path)
        was_relative = not os.path.isabs(image.filepath)
        
        # Check if this path needs remapping
        new_path = None
        
        # First check moved files
        if abs_norm in original_to_new:
            new_path = original_to_new[abs_norm]
        
        # Then check deleted files (replaced by)
        elif abs_norm in original_to_replacement:
            new_path = original_to_replacement[abs_norm]
        
        if new_path:
            # Convert to relative path if original was relative
            if was_relative:
                try:
                    rel_path = bpy.path.relpath(new_path, blend_file_dir)
                    image.filepath = rel_path
                except:
                    # Fallback to absolute if relative conversion fails
                    image.filepath = new_path
            else:
                image.filepath = new_path
            
            print(f"  Remapped: {os.path.basename(abs_path)} -> {os.path.basename(new_path)}")
            remapped_count += 1
        else:
            # Check if file exists - if not, it might be a broken reference
            if not os.path.exists(abs_path):
                not_found_count += 1
    
    # Also check material node trees for image texture nodes
    # Note: Most image texture nodes reference the image datablock (which we already remapped above),
    # but we check node.filepath for completeness in case any nodes have direct file paths
    print("\nRemapping material node texture paths...")
    node_remapped_count = 0
    for material in bpy.data.materials:
        if not material.use_nodes or not material.node_tree:
            continue
        
        for node in material.node_tree.nodes:
            if node.type == 'TEX_IMAGE' and hasattr(node, 'filepath') and node.filepath:
                abs_path = bpy.path.abspath(node.filepath)
                if abs_path:
                    abs_norm = normalize_path(abs_path)
                    
                    new_path = None
                    if abs_norm in original_to_new:
                        new_path = original_to_new[abs_norm]
                    elif abs_norm in original_to_replacement:
                        new_path = original_to_replacement[abs_norm]
                    
                    if new_path:
                        node.filepath = new_path
                        print(f"  Remapped node texture: {os.path.basename(abs_path)} -> {os.path.basename(new_path)}")
                        node_remapped_count += 1
    
    if node_remapped_count > 0:
        remapped_count += node_remapped_count
    
    # Convert any remaining absolute paths in image datablocks to relative
    # (ignoring images from linked files)
    print("\nConverting absolute image paths to relative...")
    abs_to_rel_count = 0
    for image in bpy.data.images:
        # Skip images from linked files (library datablocks)
        if image.library:
            continue
        
        if not image.filepath:
            continue
        
        # Check if path is absolute
        if os.path.isabs(image.filepath):
            try:
                # Convert to relative path
                rel_path = bpy.path.relpath(image.filepath, blend_file_dir)
                if rel_path != image.filepath:
                    image.filepath = rel_path
                    print(f"  Converted to relative: {os.path.basename(image.filepath)}")
                    abs_to_rel_count += 1
            except Exception as e:
                # If conversion fails, skip it (might be a network path or other issue)
                pass
    
    if abs_to_rel_count > 0:
        remapped_count += abs_to_rel_count
    
    print(f"\n=== REMAPPING SUMMARY ===")
    print(f"Paths remapped: {remapped_count}")
    print(f"Broken references (not found): {not_found_count}")
    
    # Make all paths relative after remapping (this handles other datablock types)
    try:
        print(f"\nMaking paths relative...")
        bpy.ops.file.make_paths_relative()
        print(f"  Converted absolute paths to relative paths")
    except Exception as e:
        print(f"  Warning: Failed to make paths relative: {e}")
    
    # Save the blend file
    try:
        print(f"\nSaving blend file...")
        # Ensure we use an absolute path
        abs_blend_path = os.path.abspath(blend_file_path)
        print(f"  Saving to: {abs_blend_path}")
        bpy.ops.wm.save_mainfile(filepath=abs_blend_path)
        
        # Verify the file was actually saved
        if os.path.exists(abs_blend_path):
            file_time = os.path.getmtime(abs_blend_path)
            print(f"Successfully saved: {abs_blend_path}")
            print(f"  File modified time: {file_time}")
            return remapped_count
        else:
            print(f"ERROR: File was not created at {abs_blend_path}")
            return -1
    except Exception as e:
        print(f"ERROR: Failed to save blend file: {e}")
        import traceback
        traceback.print_exc()
        return -1

def main():
    # Get command line arguments (after --)
    if '--' in sys.argv:
        args = sys.argv[sys.argv.index('--') + 1:]
    else:
        args = sys.argv[1:]
    
    if len(args) < 2:
        print("ERROR: Usage: blender --background --factory-startup --python remap_texture_paths.py -- [blend_file_path] [texture_moves_json_path]")
        return 1
    
    blend_file_path = args[0]
    move_log_path = args[1]
    
    # Validate paths
    if not os.path.exists(blend_file_path):
        print(f"ERROR: Blend file not found: {blend_file_path}")
        return 1
    
    if not os.path.exists(move_log_path):
        print(f"ERROR: Move log file not found: {move_log_path}")
        return 1
    
    # Load the blend file
    try:
        print(f"Loading blend file: {blend_file_path}")
        bpy.ops.wm.open_mainfile(filepath=blend_file_path)
    except Exception as e:
        print(f"ERROR: Failed to load blend file: {e}")
        return 1
    
    # Remap texture paths
    result = remap_texture_paths(blend_file_path, move_log_path)
    
    if result >= 0:
        print("\nTexture path remapping completed successfully!")
        return 0
    else:
        print("\nTexture path remapping failed!")
        return 1

if __name__ == "__main__":
    exit_code = main()
    sys.exit(exit_code)