using UnityEngine;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

/// <summary>
/// Unity Editor Tool for transferring components, settings, and child GameObjects
/// from one character prefab to another for combat/fighting games.
/// Uses EditorApplication.update coroutine pattern to prevent editor freezing.
/// </summary>
public class CharacterPrefabTransferTool : EditorWindow
{
    #region Fields
    
    // Prefab references
    private GameObject sourcePrefab;
    private GameObject targetPrefab;
    
    // Scan results
    private PrefabScanResult scanResult;
    private bool hasScanned = false;
    
    // UI State
    private Vector2 scrollPosition;
    private Vector2 logScrollPosition;
    private List<LogEntry> logEntries = new List<LogEntry>();
    
    // Foldout states
    private bool showRootComponents = true;
    private bool showChildObjects = true;
    private bool showColliders = true;
    private bool showAnimator = true;
    private bool showCamera = true;
    private bool showEffects = true;
    private bool showRigidbody = true;
    private bool showLogs = true;
    
    // Copy options (checkboxes)
    private bool copyRootComponents = true;
    private bool copyChildObjects = true;
    private bool copyColliders = true;
    private bool copyAnimator = true;
    private bool copyCamera = true;
    private bool copyEffects = true;
    private bool copyRigidbody = true;
    private bool copyMaterials = true;
    private bool copyLayersAndTags = true;
    
    // Bone matching
    private bool matchBonesByName = true;
    private Dictionary<string, string> boneMapping = new Dictionary<string, string>();
    
    // Progress - using EditorApplication.update coroutine pattern
    private float progress = 0f;
    private string progressMessage = "";
    private bool isProcessing = false;
    private IEnumerator activeCoroutine = null;
    private bool wasCancelled = false;
    
    // Bone ratio data for smart proportional scaling
    private Dictionary<string, float> limbRatios = new Dictionary<string, float>();
    private float overallScaleRatio = 1f;
    private bool boneRatiosCalculated = false;
    
    // Styles
    private GUIStyle headerStyle;
    private GUIStyle successStyle;
    private GUIStyle warningStyle;
    private GUIStyle errorStyle;
    private GUIStyle boxStyle;
    private bool stylesInitialized = false;
    
    #endregion
    
    #region Data Classes
    
    [Serializable]
    public class PrefabScanResult
    {
        public List<ComponentInfo> rootComponents = new List<ComponentInfo>();
        public List<ChildObjectInfo> childObjects = new List<ChildObjectInfo>();
        public List<ColliderInfo> colliders = new List<ColliderInfo>();
        public AnimatorInfo animatorInfo;
        public CameraInfo cameraInfo;
        public RigidbodyInfo rigidbodyInfo;
        public List<EffectInfo> effects = new List<EffectInfo>();
        public int totalComponentCount;
        public int totalChildCount;
    }
    
    [Serializable]
    public class ComponentInfo
    {
        public string typeName;
        public string fullTypeName;
        public bool isEnabled;
        public List<FieldValueInfo> serializedFields = new List<FieldValueInfo>();
        public bool shouldCopy = true;
    }
    
    [Serializable]
    public class FieldValueInfo
    {
        public string fieldName;
        public string fieldType;
        public string valuePreview;
        public bool isReference;
    }
    
    [Serializable]
    public class ChildObjectInfo
    {
        public string name;
        public string path;
        public string tag;
        public int layer;
        public Vector3 localPosition;
        public Quaternion localRotation;
        public Vector3 localScale;
        public List<ComponentInfo> components = new List<ComponentInfo>();
        public List<ChildObjectInfo> children = new List<ChildObjectInfo>();
        public bool shouldCopy = true;
        public string parentBoneName;
    }
    
    [Serializable]
    public class ColliderInfo
    {
        public string gameObjectName;
        public string gameObjectPath;
        public string colliderType;
        public bool isTrigger;
        public Vector3 center;
        public Vector3 size; // For BoxCollider
        public float radius; // For Sphere/Capsule
        public float height; // For Capsule
        public int direction; // For Capsule
        public string physicsMaterial;
        public bool shouldCopy = true;
    }
    
    [Serializable]
    public class AnimatorInfo
    {
        public string controllerName;
        public string controllerPath;
        public string avatarName;
        public bool applyRootMotion;
        public AnimatorUpdateMode updateMode;
        public AnimatorCullingMode cullingMode;
        public List<AnimatorParameterInfo> parameters = new List<AnimatorParameterInfo>();
        public bool hasAnimator = false;
    }
    
    [Serializable]
    public class AnimatorParameterInfo
    {
        public string name;
        public AnimatorControllerParameterType type;
        public float defaultFloat;
        public int defaultInt;
        public bool defaultBool;
    }
    
    [Serializable]
    public class CameraInfo
    {
        public bool hasCamera = false;
        public string gameObjectName;
        public Vector3 localPosition;
        public Quaternion localRotation;
        public float fieldOfView;
        public float nearClipPlane;
        public float farClipPlane;
        public bool orthographic;
        public float orthographicSize;
        public int depth;
        public CameraClearFlags clearFlags;
        public Color backgroundColor;
        public List<ComponentInfo> additionalComponents = new List<ComponentInfo>();
    }
    
    [Serializable]
    public class RigidbodyInfo
    {
        public bool hasRigidbody = false;
        public float mass;
        public float drag;
        public float angularDrag;
        public bool useGravity;
        public bool isKinematic;
        public RigidbodyInterpolation interpolation;
        public CollisionDetectionMode collisionDetection;
        public RigidbodyConstraints constraints;
    }
    
    [Serializable]
    public class EffectInfo
    {
        public string name;
        public string path;
        public string effectType; // ParticleSystem, TrailRenderer, etc.
        public bool isActive;
        public bool shouldCopy = true;
    }
    
    public enum LogType
    {
        Info,
        Success,
        Warning,
        Error
    }
    
    public class LogEntry
    {
        public string message;
        public LogType type;
        public DateTime timestamp;
        
        public LogEntry(string msg, LogType logType)
        {
            message = msg;
            type = logType;
            timestamp = DateTime.Now;
        }
    }
    
    #endregion
    
    #region Menu and Window
    
    [MenuItem("Tools/Character Prefab Transfer Tool")]
    public static void ShowWindow()
    {
        var window = GetWindow<CharacterPrefabTransferTool>("Character Prefab Transfer");
        window.minSize = new Vector2(500, 600);
        window.Show();
    }
    
    private void InitStyles()
    {
        if (stylesInitialized) return;
        
        headerStyle = new GUIStyle(EditorStyles.boldLabel)
        {
            fontSize = 14,
            alignment = TextAnchor.MiddleCenter
        };
        
        successStyle = new GUIStyle(EditorStyles.label)
        {
            normal = { textColor = new Color(0.2f, 0.8f, 0.2f) }
        };
        
        warningStyle = new GUIStyle(EditorStyles.label)
        {
            normal = { textColor = new Color(0.9f, 0.7f, 0.1f) }
        };
        
        errorStyle = new GUIStyle(EditorStyles.label)
        {
            normal = { textColor = new Color(0.9f, 0.2f, 0.2f) }
        };
        
        boxStyle = new GUIStyle("box")
        {
            padding = new RectOffset(10, 10, 10, 10)
        };
        
        stylesInitialized = true;
    }
    
    #endregion
    
    #region Main GUI
    
    private void OnGUI()
    {
        InitStyles();
        
        scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
        
        DrawHeader();
        DrawPrefabSelection();
        DrawActionButtons();
        
        if (hasScanned && scanResult != null)
        {
            DrawScanResults();
            DrawCopyOptions();
        }
        
        DrawLogSection();
        
        if (isProcessing)
        {
            DrawProgressBar();
        }
        
        EditorGUILayout.EndScrollView();
    }
    
    private void DrawHeader()
    {
        EditorGUILayout.Space(10);
        EditorGUILayout.LabelField("Character Prefab Transfer Tool", headerStyle);
        EditorGUILayout.Space(5);
        
        EditorGUILayout.HelpBox(
            "Transfer components, colliders, and child objects from an old character prefab to a new one.\n" +
            "1. Assign your source (old) and target (new) prefabs\n" +
            "2. Click 'Scan Source' to analyze the source prefab\n" +
            "3. Review and select what to copy\n" +
            "4. Click 'Copy All to Target' to transfer",
            MessageType.Info);
        
        EditorGUILayout.Space(10);
    }
    
    private void DrawPrefabSelection()
    {
        EditorGUILayout.BeginVertical(boxStyle);
        
        EditorGUILayout.LabelField("Prefab Selection", EditorStyles.boldLabel);
        EditorGUILayout.Space(5);
        
        EditorGUI.BeginChangeCheck();
        sourcePrefab = (GameObject)EditorGUILayout.ObjectField(
            "Source Character Prefab",
            sourcePrefab,
            typeof(GameObject),
            false);
        
        if (EditorGUI.EndChangeCheck())
        {
            hasScanned = false;
            scanResult = null;
        }
        
        targetPrefab = (GameObject)EditorGUILayout.ObjectField(
            "Target Character Prefab",
            targetPrefab,
            typeof(GameObject),
            false);
        
        // Validation messages
        if (sourcePrefab != null && !PrefabUtility.IsPartOfPrefabAsset(sourcePrefab))
        {
            EditorGUILayout.HelpBox("Source must be a prefab asset, not a scene object.", MessageType.Warning);
        }
        
        if (targetPrefab != null && !PrefabUtility.IsPartOfPrefabAsset(targetPrefab))
        {
            EditorGUILayout.HelpBox("Target must be a prefab asset, not a scene object.", MessageType.Warning);
        }
        
        EditorGUILayout.EndVertical();
    }
    
    private void DrawActionButtons()
    {
        EditorGUILayout.Space(10);
        EditorGUILayout.BeginHorizontal();
        
        GUI.enabled = sourcePrefab != null && !isProcessing;
        if (GUILayout.Button("Scan Source", GUILayout.Height(30)))
        {
            StartCoroutine(ScanSourceCoroutine());
        }
        
        GUI.enabled = hasScanned && targetPrefab != null && !isProcessing;
        if (GUILayout.Button("Copy All to Target", GUILayout.Height(30)))
        {
            if (EditorUtility.DisplayDialog("Confirm Copy",
                "This will modify the target prefab. A backup will be created.\n\nContinue?",
                "Yes, Copy", "Cancel"))
            {
                StartCoroutine(CopyToTargetCoroutine());
            }
        }
        
        GUI.enabled = true;
        
        EditorGUILayout.EndHorizontal();
        
        // Additional options
        EditorGUILayout.BeginHorizontal();
        
        if (GUILayout.Button("Clear Log", GUILayout.Height(25)))
        {
            logEntries.Clear();
        }
        
        if (GUILayout.Button("Reset All", GUILayout.Height(25)))
        {
            ResetTool();
        }
        
        EditorGUILayout.EndHorizontal();
    }
    
    private void DrawProgressBar()
    {
        EditorGUILayout.Space(10);
        
        // Status indicator
        EditorGUILayout.BeginHorizontal();
        
        // Animated spinner effect using time
        string[] spinChars = { "◐", "◓", "◑", "◒" };
        int spinIndex = (int)(EditorApplication.timeSinceStartup * 4) % spinChars.Length;
        
        GUIStyle spinStyle = new GUIStyle(EditorStyles.boldLabel)
        {
            fontSize = 16,
            alignment = TextAnchor.MiddleCenter
        };
        
        EditorGUILayout.LabelField(spinChars[spinIndex], spinStyle, GUILayout.Width(25));
        EditorGUILayout.LabelField("Processing...", EditorStyles.boldLabel);
        EditorGUILayout.EndHorizontal();
        
        // Progress bar
        Rect rect = EditorGUILayout.GetControlRect(false, 25);
        EditorGUI.ProgressBar(rect, progress, $"{(progress * 100):F0}% - {progressMessage}");
        
        EditorGUILayout.Space(5);
    }
    
    private void DrawLogSection()
    {
        EditorGUILayout.Space(10);
        
        showLogs = EditorGUILayout.Foldout(showLogs, $"Log ({logEntries.Count} entries)", true);
        
        if (showLogs && logEntries.Count > 0)
        {
            EditorGUILayout.BeginVertical(boxStyle);
            
            logScrollPosition = EditorGUILayout.BeginScrollView(logScrollPosition, GUILayout.MaxHeight(200));
            
            for (int i = logEntries.Count - 1; i >= 0; i--)
            {
                var entry = logEntries[i];
                GUIStyle style = entry.type switch
                {
                    LogType.Success => successStyle,
                    LogType.Warning => warningStyle,
                    LogType.Error => errorStyle,
                    _ => EditorStyles.label
                };
                
                string icon = entry.type switch
                {
                    LogType.Success => "✓",
                    LogType.Warning => "⚠",
                    LogType.Error => "✗",
                    _ => "•"
                };
                
                EditorGUILayout.LabelField($"{icon} [{entry.timestamp:HH:mm:ss}] {entry.message}", style);
            }
            
            EditorGUILayout.EndScrollView();
            EditorGUILayout.EndVertical();
        }
    }
    
    #endregion
    
    #region Scan Results UI
    
    private void DrawScanResults()
    {
        EditorGUILayout.Space(10);
        EditorGUILayout.LabelField("Scan Results", EditorStyles.boldLabel);
        
        EditorGUILayout.BeginVertical(boxStyle);
        EditorGUILayout.LabelField($"Total Components: {scanResult.totalComponentCount}");
        EditorGUILayout.LabelField($"Total Child Objects: {scanResult.totalChildCount}");
        EditorGUILayout.EndVertical();
        
        // Root Components
        DrawRootComponentsSection();
        
        // Child Objects
        DrawChildObjectsSection();
        
        // Colliders
        DrawCollidersSection();
        
        // Animator
        DrawAnimatorSection();
        
        // Camera
        DrawCameraSection();
        
        // Rigidbody
        DrawRigidbodySection();
        
        // Effects
        DrawEffectsSection();
    }
    
    private void DrawRootComponentsSection()
    {
        showRootComponents = EditorGUILayout.Foldout(showRootComponents, 
            $"Root Components ({scanResult.rootComponents.Count})", true);
        
        if (showRootComponents)
        {
            EditorGUI.indentLevel++;
            foreach (var comp in scanResult.rootComponents)
            {
                EditorGUILayout.BeginHorizontal();
                comp.shouldCopy = EditorGUILayout.Toggle(comp.shouldCopy, GUILayout.Width(20));
                string enabledStatus = comp.isEnabled ? "" : " [Disabled]";
                EditorGUILayout.LabelField($"{comp.typeName}{enabledStatus}");
                EditorGUILayout.EndHorizontal();
            }
            EditorGUI.indentLevel--;
        }
    }
    
    private void DrawChildObjectsSection()
    {
        showChildObjects = EditorGUILayout.Foldout(showChildObjects, 
            $"Child Objects ({scanResult.childObjects.Count})", true);
        
        if (showChildObjects)
        {
            EditorGUI.indentLevel++;
            foreach (var child in scanResult.childObjects)
            {
                DrawChildObjectRecursive(child, 0);
            }
            EditorGUI.indentLevel--;
        }
    }
    
    private void DrawChildObjectRecursive(ChildObjectInfo child, int depth)
    {
        EditorGUILayout.BeginHorizontal();
        GUILayout.Space(depth * 20);
        child.shouldCopy = EditorGUILayout.Toggle(child.shouldCopy, GUILayout.Width(20));
        
        string info = $"{child.name}";
        if (!string.IsNullOrEmpty(child.tag) && child.tag != "Untagged")
            info += $" [{child.tag}]";
        if (child.components.Count > 0)
            info += $" ({child.components.Count} components)";
        
        EditorGUILayout.LabelField(info);
        EditorGUILayout.EndHorizontal();
        
        foreach (var subChild in child.children)
        {
            DrawChildObjectRecursive(subChild, depth + 1);
        }
    }
    
    private void DrawCollidersSection()
    {
        showColliders = EditorGUILayout.Foldout(showColliders, 
            $"Colliders ({scanResult.colliders.Count})", true);
        
        if (showColliders)
        {
            EditorGUI.indentLevel++;
            foreach (var col in scanResult.colliders)
            {
                EditorGUILayout.BeginHorizontal();
                col.shouldCopy = EditorGUILayout.Toggle(col.shouldCopy, GUILayout.Width(20));
                string trigger = col.isTrigger ? " (Trigger)" : "";
                EditorGUILayout.LabelField($"{col.gameObjectName}: {col.colliderType}{trigger}");
                EditorGUILayout.EndHorizontal();
            }
            EditorGUI.indentLevel--;
        }
    }
    
    private void DrawAnimatorSection()
    {
        if (!scanResult.animatorInfo.hasAnimator) return;
        
        showAnimator = EditorGUILayout.Foldout(showAnimator, "Animator", true);
        
        if (showAnimator)
        {
            EditorGUI.indentLevel++;
            EditorGUILayout.LabelField($"Controller: {scanResult.animatorInfo.controllerName}");
            EditorGUILayout.LabelField($"Avatar: {scanResult.animatorInfo.avatarName}");
            EditorGUILayout.LabelField($"Root Motion: {scanResult.animatorInfo.applyRootMotion}");
            EditorGUILayout.LabelField($"Parameters: {scanResult.animatorInfo.parameters.Count}");
            EditorGUI.indentLevel--;
        }
    }
    
    private void DrawCameraSection()
    {
        if (!scanResult.cameraInfo.hasCamera) return;
        
        showCamera = EditorGUILayout.Foldout(showCamera, "Camera", true);
        
        if (showCamera)
        {
            EditorGUI.indentLevel++;
            EditorGUILayout.LabelField($"GameObject: {scanResult.cameraInfo.gameObjectName}");
            EditorGUILayout.LabelField($"FOV: {scanResult.cameraInfo.fieldOfView}");
            EditorGUILayout.LabelField($"Position: {scanResult.cameraInfo.localPosition}");
            EditorGUILayout.LabelField($"Additional Components: {scanResult.cameraInfo.additionalComponents.Count}");
            EditorGUI.indentLevel--;
        }
    }
    
    private void DrawRigidbodySection()
    {
        if (!scanResult.rigidbodyInfo.hasRigidbody) return;
        
        showRigidbody = EditorGUILayout.Foldout(showRigidbody, "Rigidbody", true);
        
        if (showRigidbody)
        {
            EditorGUI.indentLevel++;
            EditorGUILayout.LabelField($"Mass: {scanResult.rigidbodyInfo.mass}");
            EditorGUILayout.LabelField($"Drag: {scanResult.rigidbodyInfo.drag}");
            EditorGUILayout.LabelField($"Kinematic: {scanResult.rigidbodyInfo.isKinematic}");
            EditorGUILayout.LabelField($"Use Gravity: {scanResult.rigidbodyInfo.useGravity}");
            EditorGUI.indentLevel--;
        }
    }
    
    private void DrawEffectsSection()
    {
        if (scanResult.effects.Count == 0) return;
        
        showEffects = EditorGUILayout.Foldout(showEffects, 
            $"Effects ({scanResult.effects.Count})", true);
        
        if (showEffects)
        {
            EditorGUI.indentLevel++;
            foreach (var effect in scanResult.effects)
            {
                EditorGUILayout.BeginHorizontal();
                effect.shouldCopy = EditorGUILayout.Toggle(effect.shouldCopy, GUILayout.Width(20));
                string active = effect.isActive ? "" : " [Inactive]";
                EditorGUILayout.LabelField($"{effect.name}: {effect.effectType}{active}");
                EditorGUILayout.EndHorizontal();
            }
            EditorGUI.indentLevel--;
        }
    }
    
    private void DrawCopyOptions()
    {
        EditorGUILayout.Space(10);
        EditorGUILayout.LabelField("Copy Options", EditorStyles.boldLabel);
        
        EditorGUILayout.BeginVertical(boxStyle);
        
        copyRootComponents = EditorGUILayout.Toggle("Copy Root Components", copyRootComponents);
        copyChildObjects = EditorGUILayout.Toggle("Copy Child Objects", copyChildObjects);
        copyColliders = EditorGUILayout.Toggle("Copy Colliders", copyColliders);
        copyAnimator = EditorGUILayout.Toggle("Copy Animator Settings", copyAnimator);
        copyCamera = EditorGUILayout.Toggle("Copy Camera", copyCamera);
        copyRigidbody = EditorGUILayout.Toggle("Copy Rigidbody", copyRigidbody);
        copyEffects = EditorGUILayout.Toggle("Copy Effects (Particles)", copyEffects);
        copyMaterials = EditorGUILayout.Toggle("Copy Materials", copyMaterials);
        copyLayersAndTags = EditorGUILayout.Toggle("Copy Layers & Tags", copyLayersAndTags);
        
        EditorGUILayout.Space(5);
        matchBonesByName = EditorGUILayout.Toggle("Match Bones by Name", matchBonesByName);
        
        EditorGUILayout.EndVertical();
    }
    
    #endregion
    
    #region Helper Methods
    
    private void Log(string message, LogType type = LogType.Info)
    {
        logEntries.Add(new LogEntry(message, type));
        
        switch (type)
        {
            case LogType.Error:
                Debug.LogError($"[CharacterTransfer] {message}");
                break;
            case LogType.Warning:
                Debug.LogWarning($"[CharacterTransfer] {message}");
                break;
            default:
                Debug.Log($"[CharacterTransfer] {message}");
                break;
        }
        
        Repaint();
    }
    
    private void ResetTool()
    {
        StopCoroutine();
        sourcePrefab = null;
        targetPrefab = null;
        scanResult = null;
        hasScanned = false;
        logEntries.Clear();
        progress = 0f;
        isProcessing = false;
    }
    
    private void OnDestroy()
    {
        StopCoroutine();
        EditorUtility.ClearProgressBar();
    }
    
    #endregion
    
    #region Coroutine System (EditorApplication.update driven)
    
    /// <summary>
    /// Starts an IEnumerator coroutine that is advanced one step per editor frame.
    /// This prevents editor freezing by yielding control between steps.
    /// </summary>
    private void StartCoroutine(IEnumerator routine)
    {
        StopCoroutine(); // Stop any existing coroutine
        activeCoroutine = routine;
        isProcessing = true;
        wasCancelled = false;
        EditorApplication.update += EditorUpdateTick;
    }
    
    /// <summary>
    /// Stops the running coroutine and cleans up.
    /// </summary>
    private void StopCoroutine()
    {
        if (activeCoroutine != null)
        {
            EditorApplication.update -= EditorUpdateTick;
            activeCoroutine = null;
        }
        EditorUtility.ClearProgressBar();
        isProcessing = false;
        progress = 0f;
        progressMessage = "";
        wasCancelled = false;
        Repaint();
    }
    
    /// <summary>
    /// Called every editor frame. Advances the coroutine by one step.
    /// Between frames, Unity redraws the UI so progress bars actually render.
    /// </summary>
    private void EditorUpdateTick()
    {
        if (activeCoroutine == null)
        {
            EditorApplication.update -= EditorUpdateTick;
            return;
        }
        
        try
        {
            // Check for cancellation via the progress bar
            bool cancelled = EditorUtility.DisplayCancelableProgressBar(
                "Character Prefab Transfer",
                $"{progressMessage} ({(progress * 100):F0}%)",
                progress);
            
            if (cancelled)
            {
                wasCancelled = true;
                Log("Operation cancelled by user.", LogType.Warning);
                StopCoroutine();
                EditorUtility.DisplayDialog("Cancelled",
                    "The transfer was cancelled.\nNo changes were saved to the target prefab.", "OK");
                return;
            }
            
            // Advance the coroutine by one step
            bool hasMore = activeCoroutine.MoveNext();
            
            if (!hasMore)
            {
                // Coroutine finished naturally
                StopCoroutine();
            }
            else
            {
                // Repaint to show updated progress
                Repaint();
            }
        }
        catch (Exception ex)
        {
            Log($"COROUTINE ERROR: {ex.Message}", LogType.Error);
            Log($"Stack trace: {ex.StackTrace}", LogType.Error);
            StopCoroutine();
            EditorUtility.DisplayDialog("Transfer Failed",
                $"An error occurred:\n\n{ex.Message}\n\nCheck the console and log for details.", "OK");
        }
    }
    
    /// <summary>
    /// Sets the progress values that will be shown on the next frame's progress bar.
    /// Does NOT block - the actual bar rendering happens in EditorUpdateTick.
    /// </summary>
    private void SetProgress(float value, string message)
    {
        progress = value;
        progressMessage = message;
        Debug.Log($"[CharacterTransfer] {(value * 100):F0}% - {message}");
    }
    
    #endregion
    
    #region Scanning - Coroutine Based
    
    private IEnumerator ScanSourceCoroutine()
    {
        Log("Starting scan of source prefab...", LogType.Info);
        Log($"Source prefab: {sourcePrefab.name}", LogType.Info);
        
        SetProgress(0.01f, "Initializing scan...");
        yield return null; // Let UI render
        
        scanResult = new PrefabScanResult();
        
        // Scan root components
        SetProgress(0.1f, "Scanning root components...");
        yield return null;
        ScanRootComponents();
        Log($"Found {scanResult.rootComponents.Count} root components", LogType.Info);
        
        // Scan child objects
        SetProgress(0.25f, "Scanning child objects...");
        yield return null;
        ScanChildObjects();
        
        // Scan colliders
        SetProgress(0.4f, "Scanning colliders...");
        yield return null;
        ScanColliders();
        
        // Scan animator
        SetProgress(0.55f, "Scanning animator...");
        yield return null;
        ScanAnimator();
        
        // Scan camera
        SetProgress(0.7f, "Scanning camera...");
        yield return null;
        ScanCamera();
        
        // Scan rigidbody
        SetProgress(0.8f, "Scanning rigidbody...");
        yield return null;
        ScanRigidbody();
        
        // Scan effects
        SetProgress(0.9f, "Scanning effects...");
        yield return null;
        ScanEffects();
        
        SetProgress(1f, "Scan complete!");
        hasScanned = true;
        
        Log("=== SCAN COMPLETE ===", LogType.Success);
        Log($"Total Components: {scanResult.totalComponentCount}", LogType.Success);
        Log($"Total Child Objects: {scanResult.totalChildCount}", LogType.Success);
        Log($"Colliders: {scanResult.colliders.Count}", LogType.Info);
        Log($"Effects: {scanResult.effects.Count}", LogType.Info);
        
        yield return null; // Let final progress render
        
        EditorUtility.DisplayDialog("Scan Complete",
            $"Scan of '{sourcePrefab.name}' complete!\n\n" +
            $"• Components: {scanResult.totalComponentCount}\n" +
            $"• Child Objects: {scanResult.totalChildCount}\n" +
            $"• Colliders: {scanResult.colliders.Count}\n" +
            $"• Effects: {scanResult.effects.Count}\n\n" +
            "Review the results below and click 'Copy All to Target' when ready.",
            "OK");
    }
    
    /// <summary>
    /// Legacy synchronous scan - kept as internal method called by coroutine
    /// </summary>
    private void ScanSourcePrefab()
    {
        StartCoroutine(ScanSourceCoroutine());
    }
    
    #endregion
    
    #region Copying - Coroutine Based (Non-Blocking)
    
    /// <summary>
    /// Coroutine-based copy operation. Each yield return null gives the editor
    /// a full frame to render the progress bar, preventing freezing.
    /// </summary>
    private IEnumerator CopyToTargetCoroutine()
    {
        Log("Starting copy to target prefab...", LogType.Info);
        Log($"Source: {sourcePrefab.name}, Target: {targetPrefab.name}", LogType.Info);
        
        GameObject targetInstance = null;
        string targetPath = null;
        int currentStep = 0;
        int totalSteps = 0;
        
        // ---- STEP: Create backup ----
        SetProgress(0.02f, "Creating backup of target prefab...");
        yield return null; // FRAME BREAK - lets progress bar render
        
        try
        {
            CreateBackup();
            Log("Backup created successfully.", LogType.Success);
        }
        catch (Exception ex)
        {
            Log($"Backup failed: {ex.Message}", LogType.Error);
            EditorUtility.DisplayDialog("Backup Failed", ex.Message, "OK");
            yield break; // Stop the coroutine
        }
        
        // ---- STEP: Calculate bone ratios for proportional scaling ----
        SetProgress(0.04f, "Calculating bone proportions...");
        yield return null;
        
        try
        {
            CalculateBoneRatios();
        }
        catch (Exception ex)
        {
            Log($"Warning: Could not calculate bone ratios: {ex.Message}. Using 1:1 scaling.", LogType.Warning);
            overallScaleRatio = 1f;
            boneRatiosCalculated = false;
        }
        
        // ---- STEP: Load prefab ----
        SetProgress(0.06f, "Loading target prefab for editing...");
        yield return null;
        
        try
        {
            targetPath = AssetDatabase.GetAssetPath(targetPrefab);
            Log($"Loading prefab from: {targetPath}", LogType.Info);
            targetInstance = PrefabUtility.LoadPrefabContents(targetPath);
            
            if (targetInstance == null)
            {
                throw new Exception("Failed to load target prefab contents. The prefab may be corrupted.");
            }
            
            Log("Target prefab loaded successfully.", LogType.Success);
        }
        catch (Exception ex)
        {
            Log($"Failed to load prefab: {ex.Message}", LogType.Error);
            EditorUtility.DisplayDialog("Load Failed", ex.Message, "OK");
            yield break;
        }
        
        // ---- Calculate steps ----
        if (copyRootComponents) totalSteps++;
        if (copyChildObjects) totalSteps++;
        if (copyAnimator) totalSteps++;
        if (copyCamera) totalSteps++;
        if (copyRigidbody) totalSteps++;
        if (copyEffects) totalSteps++;
        if (copyLayersAndTags) totalSteps++;
        
        if (totalSteps == 0)
        {
            Log("No copy options selected!", LogType.Warning);
            PrefabUtility.UnloadPrefabContents(targetInstance);
            yield break;
        }
        
        float baseProgress = 0.10f;
        float stepSize = 0.75f / Mathf.Max(1, totalSteps);
        
        // ---- STEP: Copy Root Components ----
        if (copyRootComponents)
        {
            float stepProgress = baseProgress + (currentStep * stepSize);
            SetProgress(stepProgress, $"Copying root components... (Step {currentStep + 1}/{totalSteps})");
            yield return null; // FRAME BREAK
            
            // Copy each component with a yield between heavy ones
            foreach (var compInfo in scanResult.rootComponents)
            {
                if (!compInfo.shouldCopy) continue;
                try { CopySingleRootComponent(targetInstance, compInfo); }
                catch (Exception ex) { Log($"Error copying {compInfo.typeName}: {ex.Message}", LogType.Error); }
                
                // Update sub-progress
                SetProgress(stepProgress + stepSize * 0.5f, $"Copying component: {compInfo.typeName}");
                yield return null; // FRAME BREAK per component
            }
            
            currentStep++;
            Log($"Root components copied. ({currentStep}/{totalSteps})", LogType.Success);
        }
        
        // ---- STEP: Copy Child Objects ----
        if (copyChildObjects)
        {
            float stepProgress = baseProgress + (currentStep * stepSize);
            SetProgress(stepProgress, $"Copying child objects... (Step {currentStep + 1}/{totalSteps})");
            yield return null; // FRAME BREAK
            
            int childIdx = 0;
            foreach (var childInfo in scanResult.childObjects)
            {
                if (!childInfo.shouldCopy) continue;
                
                SetProgress(stepProgress + (stepSize * childIdx / Mathf.Max(1, scanResult.childObjects.Count)),
                    $"Copying child: {childInfo.name}");
                yield return null; // FRAME BREAK per child
                
                try { CopyChildObjectRecursive(childInfo, targetInstance.transform, sourcePrefab.transform); }
                catch (Exception ex) { Log($"Error copying child {childInfo.name}: {ex.Message}", LogType.Error); }
                childIdx++;
            }
            
            currentStep++;
            Log($"Child objects copied. ({currentStep}/{totalSteps})", LogType.Success);
        }
        
        // ---- STEP: Copy Animator ----
        if (copyAnimator)
        {
            float stepProgress = baseProgress + (currentStep * stepSize);
            SetProgress(stepProgress, $"Copying animator settings... (Step {currentStep + 1}/{totalSteps})");
            yield return null; // FRAME BREAK
            
            try
            {
                CopyAnimatorSettings(targetInstance);
                currentStep++;
                Log($"Animator settings copied. ({currentStep}/{totalSteps})", LogType.Success);
            }
            catch (Exception ex)
            {
                Log($"Error copying animator: {ex.Message}", LogType.Error);
                currentStep++;
            }
        }
        
        // ---- STEP: Copy Camera ----
        if (copyCamera)
        {
            float stepProgress = baseProgress + (currentStep * stepSize);
            SetProgress(stepProgress, $"Copying camera settings... (Step {currentStep + 1}/{totalSteps})");
            yield return null; // FRAME BREAK
            
            try
            {
                CopyCameraSettings(targetInstance);
                currentStep++;
                Log($"Camera settings copied. ({currentStep}/{totalSteps})", LogType.Success);
            }
            catch (Exception ex)
            {
                Log($"Error copying camera: {ex.Message}", LogType.Error);
                currentStep++;
            }
        }
        
        // ---- STEP: Copy Rigidbody ----
        if (copyRigidbody)
        {
            float stepProgress = baseProgress + (currentStep * stepSize);
            SetProgress(stepProgress, $"Copying rigidbody settings... (Step {currentStep + 1}/{totalSteps})");
            yield return null; // FRAME BREAK
            
            try
            {
                CopyRigidbodySettings(targetInstance);
                currentStep++;
                Log($"Rigidbody settings copied. ({currentStep}/{totalSteps})", LogType.Success);
            }
            catch (Exception ex)
            {
                Log($"Error copying rigidbody: {ex.Message}", LogType.Error);
                currentStep++;
            }
        }
        
        // ---- STEP: Copy Effects ----
        if (copyEffects)
        {
            float stepProgress = baseProgress + (currentStep * stepSize);
            SetProgress(stepProgress, $"Copying effects... (Step {currentStep + 1}/{totalSteps})");
            yield return null; // FRAME BREAK
            
            int effectIdx = 0;
            foreach (var effect in scanResult.effects)
            {
                if (!effect.shouldCopy) continue;
                
                SetProgress(stepProgress + (stepSize * effectIdx / Mathf.Max(1, scanResult.effects.Count)),
                    $"Copying effect: {effect.name}");
                yield return null; // FRAME BREAK per effect
                
                try { CopySingleEffect(targetInstance, effect); }
                catch (Exception ex) { Log($"Error copying effect {effect.name}: {ex.Message}", LogType.Error); }
                effectIdx++;
            }
            
            currentStep++;
            Log($"Effects copied. ({currentStep}/{totalSteps})", LogType.Success);
        }
        
        // ---- STEP: Copy Layers and Tags ----
        if (copyLayersAndTags)
        {
            float stepProgress = baseProgress + (currentStep * stepSize);
            SetProgress(stepProgress, $"Copying layers and tags... (Step {currentStep + 1}/{totalSteps})");
            yield return null; // FRAME BREAK
            
            try
            {
                CopyLayersAndTags(targetInstance);
                currentStep++;
                Log($"Layers and tags copied. ({currentStep}/{totalSteps})", LogType.Success);
            }
            catch (Exception ex)
            {
                Log($"Error copying layers/tags: {ex.Message}", LogType.Error);
                currentStep++;
            }
        }
        
        // ---- STEP: Save prefab ----
        SetProgress(0.90f, "Saving modified prefab...");
        yield return null; // FRAME BREAK
        
        try
        {
            Log("Saving prefab...", LogType.Info);
            PrefabUtility.SaveAsPrefabAsset(targetInstance, targetPath);
            Log("Prefab saved successfully!", LogType.Success);
        }
        catch (Exception ex)
        {
            Log($"Failed to save prefab: {ex.Message}", LogType.Error);
            PrefabUtility.UnloadPrefabContents(targetInstance);
            EditorUtility.DisplayDialog("Save Failed", ex.Message, "OK");
            yield break;
        }
        
        // ---- STEP: Unload and refresh ----
        SetProgress(0.94f, "Unloading prefab contents...");
        yield return null;
        
        try
        {
            PrefabUtility.UnloadPrefabContents(targetInstance);
            targetInstance = null;
        }
        catch (Exception ex)
        {
            Log($"Warning: Could not unload prefab contents: {ex.Message}", LogType.Warning);
        }
        
        SetProgress(0.97f, "Refreshing asset database...");
        yield return null;
        
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        
        SetProgress(1f, "Transfer complete!");
        Log("=== TRANSFER COMPLETE ===", LogType.Success);
        
        yield return null; // Let the final 100% render
        
        // Show completion dialog (this is the last thing)
        EditorUtility.DisplayDialog("Transfer Complete",
            $"Successfully transferred components from:\n'{sourcePrefab.name}'\nto:\n'{targetPrefab.name}'\n\n" +
            $"Completed {currentStep} operations.\nCheck the log for details.", "OK");
    }
    
    /// <summary>
    /// Legacy synchronous entry point - now just starts the coroutine
    /// </summary>
    private void CopyToTarget()
    {
        StartCoroutine(CopyToTargetCoroutine());
    }
    
    #endregion
    
    // Part 2 and 3 methods will be added below
    
    #region Scanning Implementation
    
    private void ScanRootComponents()
    {
        Component[] components = sourcePrefab.GetComponents<Component>();
        foreach (var comp in components)
        {
            if (comp == null) continue;
            if (comp is Transform) continue; // Skip transform
            
            var info = new ComponentInfo
            {
                typeName = comp.GetType().Name,
                fullTypeName = comp.GetType().FullName,
                isEnabled = IsComponentEnabled(comp)
            };
            
            // Get serialized fields
            var serializedObj = new SerializedObject(comp);
            var prop = serializedObj.GetIterator();
            while (prop.NextVisible(true))
            {
                if (prop.name == "m_Script") continue;
                
                info.serializedFields.Add(new FieldValueInfo
                {
                    fieldName = prop.name,
                    fieldType = prop.type,
                    valuePreview = GetPropertyValuePreview(prop),
                    isReference = prop.propertyType == SerializedPropertyType.ObjectReference
                });
            }
            
            scanResult.rootComponents.Add(info);
        }
        
        scanResult.totalComponentCount = scanResult.rootComponents.Count;
        Log($"Found {scanResult.rootComponents.Count} root components", LogType.Info);
    }
    
    private void ScanChildObjects()
    {
        int childCount = 0;
        foreach (Transform child in sourcePrefab.transform)
        {
            var childInfo = ScanChildRecursive(child, "");
            scanResult.childObjects.Add(childInfo);
            childCount += CountChildren(childInfo);
        }
        
        scanResult.totalChildCount = childCount;
        Log($"Found {childCount} child objects", LogType.Info);
    }
    
    private ChildObjectInfo ScanChildRecursive(Transform obj, string parentPath)
    {
        string path = string.IsNullOrEmpty(parentPath) ? obj.name : $"{parentPath}/{obj.name}";
        
        var info = new ChildObjectInfo
        {
            name = obj.name,
            path = path,
            tag = obj.tag,
            layer = obj.gameObject.layer,
            localPosition = obj.localPosition,
            localRotation = obj.localRotation,
            localScale = obj.localScale,
            parentBoneName = obj.parent != null ? obj.parent.name : ""
        };
        
        // Scan components on this child
        Component[] components = obj.GetComponents<Component>();
        foreach (var comp in components)
        {
            if (comp == null || comp is Transform) continue;
            
            var compInfo = new ComponentInfo
            {
                typeName = comp.GetType().Name,
                fullTypeName = comp.GetType().FullName,
                isEnabled = IsComponentEnabled(comp)
            };
            info.components.Add(compInfo);
        }
        
        // Scan children recursively
        foreach (Transform child in obj)
        {
            info.children.Add(ScanChildRecursive(child, path));
        }
        
        return info;
    }
    
    private int CountChildren(ChildObjectInfo info)
    {
        int count = 1;
        foreach (var child in info.children)
        {
            count += CountChildren(child);
        }
        return count;
    }
    
    private void ScanColliders()
    {
        Collider[] colliders = sourcePrefab.GetComponentsInChildren<Collider>(true);
        foreach (var col in colliders)
        {
            var info = new ColliderInfo
            {
                gameObjectName = col.gameObject.name,
                gameObjectPath = GetGameObjectPath(col.transform, sourcePrefab.transform),
                colliderType = col.GetType().Name,
                isTrigger = col.isTrigger,
                physicsMaterial = col.sharedMaterial != null ? col.sharedMaterial.name : "None"
            };
            
            switch (col)
            {
                case BoxCollider box:
                    info.center = box.center;
                    info.size = box.size;
                    break;
                case SphereCollider sphere:
                    info.center = sphere.center;
                    info.radius = sphere.radius;
                    break;
                case CapsuleCollider capsule:
                    info.center = capsule.center;
                    info.radius = capsule.radius;
                    info.height = capsule.height;
                    info.direction = capsule.direction;
                    break;
            }
            
            scanResult.colliders.Add(info);
        }
        
        Log($"Found {scanResult.colliders.Count} colliders", LogType.Info);
    }
    
    private void ScanAnimator()
    {
        Animator animator = sourcePrefab.GetComponent<Animator>();
        if (animator == null)
        {
            scanResult.animatorInfo = new AnimatorInfo { hasAnimator = false };
            return;
        }
        
        scanResult.animatorInfo = new AnimatorInfo
        {
            hasAnimator = true,
            controllerName = animator.runtimeAnimatorController != null ? 
                animator.runtimeAnimatorController.name : "None",
            controllerPath = animator.runtimeAnimatorController != null ? 
                AssetDatabase.GetAssetPath(animator.runtimeAnimatorController) : "",
            avatarName = animator.avatar != null ? animator.avatar.name : "None",
            applyRootMotion = animator.applyRootMotion,
            updateMode = animator.updateMode,
            cullingMode = animator.cullingMode
        };
        
        // Get parameters if controller exists
        if (animator.runtimeAnimatorController != null)
        {
            foreach (var param in animator.parameters)
            {
                scanResult.animatorInfo.parameters.Add(new AnimatorParameterInfo
                {
                    name = param.name,
                    type = param.type,
                    defaultFloat = param.defaultFloat,
                    defaultInt = param.defaultInt,
                    defaultBool = param.defaultBool
                });
            }
        }
        
        Log($"Found animator with controller: {scanResult.animatorInfo.controllerName}", LogType.Info);
    }
    
    private void ScanCamera()
    {
        Camera cam = sourcePrefab.GetComponentInChildren<Camera>(true);
        if (cam == null)
        {
            scanResult.cameraInfo = new CameraInfo { hasCamera = false };
            return;
        }
        
        scanResult.cameraInfo = new CameraInfo
        {
            hasCamera = true,
            gameObjectName = cam.gameObject.name,
            localPosition = cam.transform.localPosition,
            localRotation = cam.transform.localRotation,
            fieldOfView = cam.fieldOfView,
            nearClipPlane = cam.nearClipPlane,
            farClipPlane = cam.farClipPlane,
            orthographic = cam.orthographic,
            orthographicSize = cam.orthographicSize,
            depth = (int)cam.depth,
            clearFlags = cam.clearFlags,
            backgroundColor = cam.backgroundColor
        };
        
        // Get additional components on camera object
        foreach (var comp in cam.GetComponents<Component>())
        {
            if (comp is Transform || comp is Camera) continue;
            
            scanResult.cameraInfo.additionalComponents.Add(new ComponentInfo
            {
                typeName = comp.GetType().Name,
                fullTypeName = comp.GetType().FullName,
                isEnabled = IsComponentEnabled(comp)
            });
        }
        
        Log($"Found camera: {scanResult.cameraInfo.gameObjectName}", LogType.Info);
    }
    
    private void ScanRigidbody()
    {
        Rigidbody rb = sourcePrefab.GetComponent<Rigidbody>();
        if (rb == null)
        {
            scanResult.rigidbodyInfo = new RigidbodyInfo { hasRigidbody = false };
            return;
        }
        
        scanResult.rigidbodyInfo = new RigidbodyInfo
        {
            hasRigidbody = true,
            mass = rb.mass,
            drag = rb.linearDamping,
            angularDrag = rb.angularDamping,
            useGravity = rb.useGravity,
            isKinematic = rb.isKinematic,
            interpolation = rb.interpolation,
            collisionDetection = rb.collisionDetectionMode,
            constraints = rb.constraints
        };
        
        Log("Found rigidbody", LogType.Info);
    }
    
    private void ScanEffects()
    {
        // Scan particle systems
        ParticleSystem[] particles = sourcePrefab.GetComponentsInChildren<ParticleSystem>(true);
        foreach (var ps in particles)
        {
            scanResult.effects.Add(new EffectInfo
            {
                name = ps.gameObject.name,
                path = GetGameObjectPath(ps.transform, sourcePrefab.transform),
                effectType = "ParticleSystem",
                isActive = ps.gameObject.activeSelf
            });
        }
        
        // Scan trail renderers
        TrailRenderer[] trails = sourcePrefab.GetComponentsInChildren<TrailRenderer>(true);
        foreach (var trail in trails)
        {
            // Avoid duplicates if same object has particle system
            bool alreadyAdded = scanResult.effects.Any(e => e.path == GetGameObjectPath(trail.transform, sourcePrefab.transform));
            if (!alreadyAdded)
            {
                scanResult.effects.Add(new EffectInfo
                {
                    name = trail.gameObject.name,
                    path = GetGameObjectPath(trail.transform, sourcePrefab.transform),
                    effectType = "TrailRenderer",
                    isActive = trail.gameObject.activeSelf
                });
            }
        }
        
        Log($"Found {scanResult.effects.Count} effects", LogType.Info);
    }
    
    #endregion
    
    #region Helper Methods for Scanning
    
    private bool IsComponentEnabled(Component comp)
    {
        if (comp is Behaviour behaviour)
            return behaviour.enabled;
        if (comp is Renderer renderer)
            return renderer.enabled;
        if (comp is Collider collider)
            return collider.enabled;
        return true;
    }
    
    private string GetPropertyValuePreview(SerializedProperty prop)
    {
        return prop.propertyType switch
        {
            SerializedPropertyType.Integer => prop.intValue.ToString(),
            SerializedPropertyType.Float => prop.floatValue.ToString("F2"),
            SerializedPropertyType.Boolean => prop.boolValue.ToString(),
            SerializedPropertyType.String => prop.stringValue,
            SerializedPropertyType.Vector3 => prop.vector3Value.ToString(),
            SerializedPropertyType.ObjectReference => prop.objectReferenceValue != null ? 
                prop.objectReferenceValue.name : "null",
            _ => prop.type
        };
    }
    
    private string GetGameObjectPath(Transform obj, Transform root)
    {
        List<string> path = new List<string>();
        Transform current = obj;
        
        while (current != null && current != root)
        {
            path.Insert(0, current.name);
            current = current.parent;
        }
        
        return string.Join("/", path);
    }
    
    #endregion
    
    #region Bone Ratio System - Smart Proportional Scaling
    
    /// <summary>
    /// Maps limb names to start/end HumanBodyBones for length measurement.
    /// </summary>
    private static readonly Dictionary<string, HumanBodyBones[]> LimbBoneChains = new Dictionary<string, HumanBodyBones[]>
    {
        {"leftarm",  new[] { HumanBodyBones.LeftUpperArm,  HumanBodyBones.LeftHand }},
        {"rightarm", new[] { HumanBodyBones.RightUpperArm, HumanBodyBones.RightHand }},
        {"leftleg",  new[] { HumanBodyBones.LeftUpperLeg,  HumanBodyBones.LeftFoot }},
        {"rightleg", new[] { HumanBodyBones.RightUpperLeg, HumanBodyBones.RightFoot }},
        {"torso",    new[] { HumanBodyBones.Hips,          HumanBodyBones.Head }},
    };
    
    /// <summary>
    /// Maps collider / child-object name keywords to limb names for ratio lookup.
    /// </summary>
    private static readonly Dictionary<string, string> ColliderKeywordToLimb = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
    {
        {"lefthand",  "leftarm"},
        {"righthand", "rightarm"},
        {"leftfist",  "leftarm"},
        {"rightfist", "rightarm"},
        {"leftfoot",  "leftleg"},
        {"rightfoot", "rightleg"},
        {"leftleg",   "leftleg"},
        {"rightleg",  "rightleg"},
        {"body",      "torso"},
        {"chest",     "torso"},
        {"torso",     "torso"},
        {"head",      "torso"},
        {"spine",     "torso"},
        {"hips",      "torso"},
    };
    
    /// <summary>
    /// Calculate bone-proportional scale ratios between source and target characters.
    /// Uses Animator.GetBoneTransform for humanoid rigs, falls back to overall bounds height.
    /// </summary>
    private void CalculateBoneRatios()
    {
        limbRatios.Clear();
        overallScaleRatio = 1f;
        boneRatiosCalculated = false;
        
        // Temporarily instantiate both prefabs so Animator is initialized and GetBoneTransform works
        GameObject sourceInstance = null;
        GameObject targetInstance = null;
        
        try
        {
            sourceInstance = PrefabUtility.InstantiatePrefab(sourcePrefab) as GameObject;
            targetInstance = PrefabUtility.InstantiatePrefab(targetPrefab) as GameObject;
            
            if (sourceInstance == null || targetInstance == null)
            {
                Log("Could not instantiate prefabs for bone measurement. Using 1:1 ratio.", LogType.Warning);
                return;
            }
            
            Animator sourceAnim = sourceInstance.GetComponent<Animator>();
            Animator targetAnim = targetInstance.GetComponent<Animator>();
            
            if (sourceAnim != null && targetAnim != null &&
                sourceAnim.avatar != null && targetAnim.avatar != null &&
                sourceAnim.avatar.isHuman && targetAnim.avatar.isHuman)
            {
                // Humanoid rig - use GetBoneTransform for precise per-limb ratios
                CalculateHumanoidRatios(sourceAnim, targetAnim);
                Log("Using Humanoid bone ratios for proportional scaling.", LogType.Success);
            }
            else
            {
                // Non-humanoid or missing avatar - use overall renderer bounds ratio
                CalculateFallbackRatio(sourceInstance, targetInstance);
            }
            
            boneRatiosCalculated = true;
            Log($"Bone ratios calculated. Overall ratio: {overallScaleRatio:F3}", LogType.Success);
            foreach (var kvp in limbRatios)
            {
                Log($"  Limb '{kvp.Key}' ratio: {kvp.Value:F3}", LogType.Info);
            }
        }
        catch (Exception ex)
        {
            Log($"Error calculating bone ratios: {ex.Message}. Using 1:1 ratio.", LogType.Warning);
            overallScaleRatio = 1f;
        }
        finally
        {
            if (sourceInstance != null) DestroyImmediate(sourceInstance);
            if (targetInstance != null) DestroyImmediate(targetInstance);
        }
    }
    
    private void CalculateHumanoidRatios(Animator sourceAnim, Animator targetAnim)
    {
        // Overall height ratio (Hips to Head)
        float sourceHeight = GetLimbLength(sourceAnim, HumanBodyBones.Hips, HumanBodyBones.Head);
        float targetHeight = GetLimbLength(targetAnim, HumanBodyBones.Hips, HumanBodyBones.Head);
        
        overallScaleRatio = (sourceHeight > 0.001f) ? (targetHeight / sourceHeight) : 1f;
        
        // Per-limb ratios
        foreach (var kvp in LimbBoneChains)
        {
            float sourceLen = GetLimbLength(sourceAnim, kvp.Value[0], kvp.Value[1]);
            float targetLen = GetLimbLength(targetAnim, kvp.Value[0], kvp.Value[1]);
            
            float ratio = (sourceLen > 0.001f) ? (targetLen / sourceLen) : overallScaleRatio;
            limbRatios[kvp.Key] = ratio;
        }
    }
    
    private float GetLimbLength(Animator animator, HumanBodyBones startBone, HumanBodyBones endBone)
    {
        Transform start = animator.GetBoneTransform(startBone);
        Transform end   = animator.GetBoneTransform(endBone);
        
        if (start == null || end == null) return 0f;
        return Vector3.Distance(start.position, end.position);
    }
    
    private void CalculateFallbackRatio(GameObject sourceInstance, GameObject targetInstance)
    {
        Bounds sourceBounds = CalculateTotalBounds(sourceInstance);
        Bounds targetBounds = CalculateTotalBounds(targetInstance);
        
        float sourceHeight = sourceBounds.size.y;
        float targetHeight = targetBounds.size.y;
        
        overallScaleRatio = (sourceHeight > 0.001f) ? (targetHeight / sourceHeight) : 1f;
        
        // Use overall ratio for all limbs
        foreach (var kvp in LimbBoneChains)
        {
            limbRatios[kvp.Key] = overallScaleRatio;
        }
        
        Log("Using overall bounds ratio (non-humanoid fallback).", LogType.Info);
    }
    
    private Bounds CalculateTotalBounds(GameObject obj)
    {
        Renderer[] renderers = obj.GetComponentsInChildren<Renderer>(true);
        if (renderers.Length == 0) return new Bounds(obj.transform.position, Vector3.one);
        
        Bounds bounds = renderers[0].bounds;
        for (int i = 1; i < renderers.Length; i++)
        {
            bounds.Encapsulate(renderers[i].bounds);
        }
        return bounds;
    }
    
    /// <summary>
    /// Gets the scale ratio for a given child-object name by matching keywords to limb ratios.
    /// E.g. "lefthandcollider" → matches "lefthand" → returns leftarm ratio.
    /// </summary>
    private float GetScaleRatioForObject(string objectName)
    {
        if (!boneRatiosCalculated) return 1f;
        
        string nameLower = objectName.ToLowerInvariant().Replace(" ", "").Replace("_", "");
        
        foreach (var kvp in ColliderKeywordToLimb)
        {
            if (nameLower.Contains(kvp.Key.ToLowerInvariant()))
            {
                if (limbRatios.TryGetValue(kvp.Value, out float ratio))
                {
                    return ratio;
                }
            }
        }
        
        return overallScaleRatio;
    }
    
    /// <summary>
    /// Gets the scale ratio based on a parent bone name.
    /// E.g. bone "LeftHand" → matches left arm → returns leftarm ratio.
    /// </summary>
    private float GetScaleRatioForBone(string boneName)
    {
        if (!boneRatiosCalculated || string.IsNullOrEmpty(boneName)) return overallScaleRatio;
        
        string nameLower = boneName.ToLowerInvariant().Replace(" ", "").Replace("_", "");
        
        if (nameLower.Contains("lefthand") || nameLower.Contains("leftlowerarm") ||
            nameLower.Contains("leftupperarm") || nameLower.Contains("leftshoulder") ||
            nameLower.Contains("l_hand") || nameLower.Contains("l_arm") ||
            nameLower.Contains("lhand") || nameLower.Contains("larm"))
        {
            return limbRatios.TryGetValue("leftarm", out float r) ? r : overallScaleRatio;
        }
        if (nameLower.Contains("righthand") || nameLower.Contains("rightlowerarm") ||
            nameLower.Contains("rightupperarm") || nameLower.Contains("rightshoulder") ||
            nameLower.Contains("r_hand") || nameLower.Contains("r_arm") ||
            nameLower.Contains("rhand") || nameLower.Contains("rarm"))
        {
            return limbRatios.TryGetValue("rightarm", out float r) ? r : overallScaleRatio;
        }
        if (nameLower.Contains("leftfoot") || nameLower.Contains("leftlowerleg") ||
            nameLower.Contains("leftupperleg") || nameLower.Contains("lefttoe") ||
            nameLower.Contains("l_foot") || nameLower.Contains("l_leg") ||
            nameLower.Contains("lfoot") || nameLower.Contains("lleg"))
        {
            return limbRatios.TryGetValue("leftleg", out float r) ? r : overallScaleRatio;
        }
        if (nameLower.Contains("rightfoot") || nameLower.Contains("rightlowerleg") ||
            nameLower.Contains("rightupperleg") || nameLower.Contains("righttoe") ||
            nameLower.Contains("r_foot") || nameLower.Contains("r_leg") ||
            nameLower.Contains("rfoot") || nameLower.Contains("rleg"))
        {
            return limbRatios.TryGetValue("rightleg", out float r) ? r : overallScaleRatio;
        }
        if (nameLower.Contains("hips") || nameLower.Contains("spine") ||
            nameLower.Contains("chest") || nameLower.Contains("head") ||
            nameLower.Contains("neck") || nameLower.Contains("torso"))
        {
            return limbRatios.TryGetValue("torso", out float r) ? r : overallScaleRatio;
        }
        
        return overallScaleRatio;
    }
    
    /// <summary>
    /// Scales a collider's dimensions (center, size, radius, height) by the given ratio.
    /// </summary>
    private void ScaleCollider(Collider collider, float ratio)
    {
        if (collider == null || Mathf.Approximately(ratio, 1f)) return;
        
        switch (collider)
        {
            case BoxCollider box:
                box.center *= ratio;
                box.size *= ratio;
                break;
            case SphereCollider sphere:
                sphere.center *= ratio;
                sphere.radius *= ratio;
                break;
            case CapsuleCollider capsule:
                capsule.center *= ratio;
                capsule.radius *= ratio;
                capsule.height *= ratio;
                break;
            case MeshCollider mesh:
                Log($"MeshCollider on '{mesh.gameObject.name}' cannot be auto-scaled - manual adjustment needed.", LogType.Warning);
                break;
        }
    }
    
    #endregion
    
    #region Copying Implementation
    
    private void CreateBackup()
    {
        string targetPath = AssetDatabase.GetAssetPath(targetPrefab);
        string backupPath = targetPath.Replace(".prefab", "_backup.prefab");
        
        // Check if backup already exists
        int counter = 1;
        while (AssetDatabase.LoadAssetAtPath<GameObject>(backupPath) != null)
        {
            backupPath = targetPath.Replace(".prefab", $"_backup_{counter}.prefab");
            counter++;
        }
        
        AssetDatabase.CopyAsset(targetPath, backupPath);
        Log($"Created backup at: {backupPath}", LogType.Success);
    }
    
    /// <summary>
    /// Copies a single root component to the target (called per-component from coroutine).
    /// CRITICAL: Preserves target's root Transform - never lets source overwrite scale/position/rotation.
    /// </summary>
    private void CopySingleRootComponent(GameObject targetInstance, ComponentInfo compInfo)
    {
        try
        {
            Type compType = GetTypeByName(compInfo.fullTypeName);
            if (compType == null)
            {
                Log($"Could not find type: {compInfo.typeName}", LogType.Warning);
                return;
            }
            
            // NEVER copy Transform - target must keep its own transform values
            if (typeof(Transform).IsAssignableFrom(compType)) return;
            
            // Skip Animator - handled separately by CopyAnimatorSettings to preserve target's Avatar
            if (typeof(Animator).IsAssignableFrom(compType))
            {
                Log($"Skipping Animator in root copy (handled separately to preserve target's Avatar)", LogType.Info);
                return;
            }
            
            Component sourceComp = sourcePrefab.GetComponent(compType);
            if (sourceComp == null) return;
            
            Component targetComp = targetInstance.GetComponent(compType);
            
            if (targetComp == null)
            {
                targetComp = targetInstance.AddComponent(compType);
                if (targetComp == null)
                {
                    Log($"Failed to add component: {compInfo.typeName}", LogType.Error);
                    return;
                }
            }
            
            // CRITICAL: Save target's root Transform before CopySerialized
            // EditorUtility.CopySerialized can indirectly affect transform values
            Vector3 savedPosition = targetInstance.transform.localPosition;
            Quaternion savedRotation = targetInstance.transform.localRotation;
            Vector3 savedScale = targetInstance.transform.localScale;
            
            CopyComponentValues(sourceComp, targetComp);
            
            // CRITICAL: Restore target's root Transform - NEVER let source overwrite it
            targetInstance.transform.localPosition = savedPosition;
            targetInstance.transform.localRotation = savedRotation;
            targetInstance.transform.localScale = savedScale;
            
            SetComponentEnabled(targetComp, compInfo.isEnabled);
            Log($"Copied component: {compInfo.typeName}", LogType.Success);
        }
        catch (Exception ex)
        {
            Log($"Failed to copy {compInfo.typeName}: {ex.Message}", LogType.Error);
        }
    }
    
    private void CopyRootComponents(GameObject targetInstance)
    {
        foreach (var compInfo in scanResult.rootComponents)
        {
            if (!compInfo.shouldCopy) continue;
            CopySingleRootComponent(targetInstance, compInfo);
        }
    }
    
    private void CopyChildObjects(GameObject targetInstance)
    {
        foreach (var childInfo in scanResult.childObjects)
        {
            if (!childInfo.shouldCopy) continue;
            
            try
            {
                CopyChildObjectRecursive(childInfo, targetInstance.transform, sourcePrefab.transform);
            }
            catch (Exception ex)
            {
                Log($"Failed to copy child {childInfo.name}: {ex.Message}", LogType.Error);
            }
        }
    }
    
    /// <summary>
    /// Smart bone-proportional child object copy.
    /// - Finds the matching bone on the target skeleton (by name)
    /// - Creates FRESH GameObjects (not Object.Instantiate which carries source scale)
    /// - Copies components individually and scales colliders by the limb ratio
    /// - Only applies scaled transforms to NEW objects; existing skeleton bones are untouched
    /// </summary>
    private void CopyChildObjectRecursive(ChildObjectInfo childInfo, Transform targetParent, Transform sourceRoot)
    {
        // Find source object
        Transform sourceChild = sourceRoot.Find(childInfo.name);
        if (sourceChild == null)
        {
            sourceChild = FindByPath(sourceRoot, childInfo.path);
        }
        
        // Determine target parent bone
        Transform targetBone = targetParent;
        if (matchBonesByName && !string.IsNullOrEmpty(childInfo.parentBoneName))
        {
            Transform matchingBone = FindBoneByName(targetParent.root, childInfo.parentBoneName);
            if (matchingBone != null)
            {
                targetBone = matchingBone;
            }
        }
        
        // Check if target already has this object (e.g. existing skeleton bone)
        Transform existingChild = targetBone.Find(childInfo.name);
        if (existingChild == null)
        {
            existingChild = targetParent.Find(childInfo.name);
        }
        
        Transform targetChild;
        bool isNewObject = false;
        
        if (existingChild != null)
        {
            targetChild = existingChild;
            Log($"Found existing child: {childInfo.name} (will update components only)", LogType.Info);
        }
        else
        {
            // Create a FRESH empty GameObject — do NOT use Object.Instantiate from source
            // because Instantiate carries the source's baked transform/scale data
            GameObject newChild = new GameObject(childInfo.name);
            newChild.transform.SetParent(targetBone);
            targetChild = newChild.transform;
            isNewObject = true;
            
            if (targetBone != targetParent)
            {
                Log($"Created '{childInfo.name}' under bone: {targetBone.name}", LogType.Success);
            }
            else
            {
                Log($"Created child: {childInfo.name}", LogType.Success);
            }
        }
        
        // Calculate the proportional scale ratio for this object
        // First try matching by object name (e.g. "lefthandcollider" → leftarm ratio)
        float ratio = GetScaleRatioForObject(childInfo.name);
        // Refine with parent bone name if object name didn't give a specific match
        if (Mathf.Approximately(ratio, overallScaleRatio))
        {
            float boneRatio = GetScaleRatioForBone(childInfo.parentBoneName);
            if (!Mathf.Approximately(boneRatio, overallScaleRatio))
            {
                ratio = boneRatio;
            }
        }
        
        // Apply transform ONLY to NEW objects — never overwrite existing skeleton bone transforms
        if (isNewObject)
        {
            // Position is scaled by ratio so collider offsets match target proportions
            targetChild.localPosition = childInfo.localPosition * ratio;
            targetChild.localRotation = childInfo.localRotation;
            targetChild.localScale = childInfo.localScale; // Keep original local scale
        }
        
        // Copy components from source to target child
        if (sourceChild != null && isNewObject)
        {
            Component[] sourceComponents = sourceChild.GetComponents<Component>();
            foreach (var sourceComp in sourceComponents)
            {
                if (sourceComp == null || sourceComp is Transform) continue;
                
                Type compType = sourceComp.GetType();
                Component targetComp = targetChild.GetComponent(compType);
                
                if (targetComp == null)
                {
                    try
                    {
                        targetComp = targetChild.gameObject.AddComponent(compType);
                    }
                    catch (Exception ex)
                    {
                        Log($"Could not add {compType.Name} to {childInfo.name}: {ex.Message}", LogType.Warning);
                        continue;
                    }
                }
                
                if (targetComp != null)
                {
                    // Save child's transform before CopySerialized
                    Vector3 savedPos = targetChild.localPosition;
                    Quaternion savedRot = targetChild.localRotation;
                    Vector3 savedScale = targetChild.localScale;
                    
                    CopyComponentValues(sourceComp, targetComp);
                    
                    // Restore transform (CopySerialized can affect it indirectly)
                    targetChild.localPosition = savedPos;
                    targetChild.localRotation = savedRot;
                    targetChild.localScale = savedScale;
                }
            }
            
            // Scale all colliders on this object proportionally to target's bone dimensions
            Collider[] colliders = targetChild.GetComponents<Collider>();
            foreach (var col in colliders)
            {
                ScaleCollider(col, ratio);
                Log($"Auto-scaled {col.GetType().Name} on '{childInfo.name}' by ratio {ratio:F3}", LogType.Info);
            }
        }
        else if (sourceChild != null && !isNewObject)
        {
            // For existing objects, update collider values from source then scale
            Collider[] sourceColliders = sourceChild.GetComponents<Collider>();
            Collider[] targetColliders = targetChild.GetComponents<Collider>();
            
            for (int i = 0; i < Mathf.Min(sourceColliders.Length, targetColliders.Length); i++)
            {
                Vector3 savedPos = targetChild.localPosition;
                Quaternion savedRot = targetChild.localRotation;
                Vector3 savedScale = targetChild.localScale;
                
                CopyComponentValues(sourceColliders[i], targetColliders[i]);
                
                targetChild.localPosition = savedPos;
                targetChild.localRotation = savedRot;
                targetChild.localScale = savedScale;
                
                ScaleCollider(targetColliders[i], ratio);
            }
        }
        
        // Apply layer and tag
        if (copyLayersAndTags)
        {
            targetChild.gameObject.layer = childInfo.layer;
            try { targetChild.gameObject.tag = childInfo.tag; }
            catch { /* Tag might not exist */ }
        }
        
        // Recurse into sub-children (since we don't use Instantiate, we must build the full tree)
        foreach (var subChildInfo in childInfo.children)
        {
            if (!subChildInfo.shouldCopy) continue;
            CopyChildObjectRecursive(subChildInfo, targetChild, sourceChild != null ? sourceChild : sourceRoot);
        }
    }
    
    private void CopyAnimatorSettings(GameObject targetInstance)
    {
        if (!scanResult.animatorInfo.hasAnimator) return;
        
        Animator sourceAnimator = sourcePrefab.GetComponent<Animator>();
        Animator targetAnimator = targetInstance.GetComponent<Animator>();
        
        if (targetAnimator == null)
        {
            targetAnimator = targetInstance.AddComponent<Animator>();
        }
        
        // Copy controller
        targetAnimator.runtimeAnimatorController = sourceAnimator.runtimeAnimatorController;
        
        // Copy settings
        targetAnimator.applyRootMotion = sourceAnimator.applyRootMotion;
        targetAnimator.updateMode = sourceAnimator.updateMode;
        targetAnimator.cullingMode = sourceAnimator.cullingMode;
        
        // Note: Avatar should typically be the target's own avatar for humanoid rigs
        // but we can copy it for generic rigs or if explicitly requested
        if (targetAnimator.avatar == null)
        {
            Log("Target has no avatar - you may need to configure the avatar separately", LogType.Warning);
        }
        
        Log("Copied animator settings", LogType.Success);
    }
    
    private void CopyCameraSettings(GameObject targetInstance)
    {
        if (!scanResult.cameraInfo.hasCamera) return;
        
        // Find or create camera object
        Camera sourceCamera = sourcePrefab.GetComponentInChildren<Camera>(true);
        Camera targetCamera = targetInstance.GetComponentInChildren<Camera>(true);
        
        if (sourceCamera == null) return;
        
        if (targetCamera == null)
        {
            // Clone the entire camera object
            GameObject cameraClone = UnityEngine.Object.Instantiate(
                sourceCamera.gameObject, 
                targetInstance.transform);
            cameraClone.name = sourceCamera.gameObject.name;
            targetCamera = cameraClone.GetComponent<Camera>();
            Log("Created new camera object", LogType.Success);
        }
        else
        {
            // Update existing camera
            targetCamera.fieldOfView = sourceCamera.fieldOfView;
            targetCamera.nearClipPlane = sourceCamera.nearClipPlane;
            targetCamera.farClipPlane = sourceCamera.farClipPlane;
            targetCamera.orthographic = sourceCamera.orthographic;
            targetCamera.orthographicSize = sourceCamera.orthographicSize;
            targetCamera.depth = sourceCamera.depth;
            targetCamera.clearFlags = sourceCamera.clearFlags;
            targetCamera.backgroundColor = sourceCamera.backgroundColor;
            
            // Copy position relative to parent
            targetCamera.transform.localPosition = scanResult.cameraInfo.localPosition;
            targetCamera.transform.localRotation = scanResult.cameraInfo.localRotation;
            
            Log("Updated camera settings", LogType.Success);
        }
        
        // Copy additional camera components
        foreach (var compInfo in scanResult.cameraInfo.additionalComponents)
        {
            Type compType = GetTypeByName(compInfo.fullTypeName);
            if (compType == null) continue;
            
            Component sourceComp = sourceCamera.GetComponent(compType);
            Component targetComp = targetCamera.GetComponent(compType);
            
            if (targetComp == null && sourceComp != null)
            {
                targetComp = targetCamera.gameObject.AddComponent(compType);
            }
            
            if (sourceComp != null && targetComp != null)
            {
                CopyComponentValues(sourceComp, targetComp);
            }
        }
    }
    
    private void CopyRigidbodySettings(GameObject targetInstance)
    {
        if (!scanResult.rigidbodyInfo.hasRigidbody) return;
        
        Rigidbody targetRb = targetInstance.GetComponent<Rigidbody>();
        if (targetRb == null)
        {
            targetRb = targetInstance.AddComponent<Rigidbody>();
        }
        
        targetRb.mass = scanResult.rigidbodyInfo.mass;
        targetRb.linearDamping = scanResult.rigidbodyInfo.drag;
        targetRb.angularDamping = scanResult.rigidbodyInfo.angularDrag;
        targetRb.useGravity = scanResult.rigidbodyInfo.useGravity;
        targetRb.isKinematic = scanResult.rigidbodyInfo.isKinematic;
        targetRb.interpolation = scanResult.rigidbodyInfo.interpolation;
        targetRb.collisionDetectionMode = scanResult.rigidbodyInfo.collisionDetection;
        targetRb.constraints = scanResult.rigidbodyInfo.constraints;
        
        Log("Copied rigidbody settings", LogType.Success);
    }
    
    /// <summary>
    /// Copies a single effect to the target (called per-effect from coroutine)
    /// </summary>
    private void CopySingleEffect(GameObject targetInstance, EffectInfo effect)
    {
        try
        {
            Transform sourceEffect = FindByPath(sourcePrefab.transform, effect.path);
            if (sourceEffect == null)
            {
                sourceEffect = sourcePrefab.transform.Find(effect.name);
            }
            
            if (sourceEffect == null)
            {
                Log($"Could not find source effect: {effect.name}", LogType.Warning);
                return;
            }
            
            Transform targetParent = targetInstance.transform;
            
            if (sourceEffect.parent != null && sourceEffect.parent != sourcePrefab.transform)
            {
                string parentName = sourceEffect.parent.name;
                Transform matchingParent = FindBoneByName(targetInstance.transform, parentName);
                if (matchingParent != null)
                {
                    targetParent = matchingParent;
                }
            }
            
            Transform existingEffect = FindByPath(targetInstance.transform, effect.path);
            if (existingEffect == null)
            {
                existingEffect = targetParent.Find(effect.name);
            }
            
            if (existingEffect != null)
            {
                CopyComponentValues(
                    sourceEffect.GetComponent(effect.effectType == "ParticleSystem" ?
                        typeof(ParticleSystem) : typeof(TrailRenderer)),
                    existingEffect.GetComponent(effect.effectType == "ParticleSystem" ?
                        typeof(ParticleSystem) : typeof(TrailRenderer))
                );
                Log($"Updated effect: {effect.name}", LogType.Success);
            }
            else
            {
                GameObject cloned = UnityEngine.Object.Instantiate(sourceEffect.gameObject, targetParent);
                cloned.name = effect.name;
                cloned.SetActive(effect.isActive);
                
                // Scale position proportionally to target's skeleton
                float ratio = GetScaleRatioForBone(targetParent.name);
                cloned.transform.localPosition = sourceEffect.localPosition * ratio;
                cloned.transform.localRotation = sourceEffect.localRotation;
                
                Log($"Copied effect: {effect.name} (position scaled by {ratio:F3})", LogType.Success);
            }
        }
        catch (Exception ex)
        {
            Log($"Failed to copy effect {effect.name}: {ex.Message}", LogType.Warning);
        }
    }
    
    private void CopyEffects(GameObject targetInstance)
    {
        foreach (var effect in scanResult.effects)
        {
            if (!effect.shouldCopy) continue;
            CopySingleEffect(targetInstance, effect);
        }
    }
    
    private void CopyLayersAndTags(GameObject targetInstance)
    {
        // Copy root object layer and tag
        targetInstance.layer = sourcePrefab.layer;
        try { targetInstance.tag = sourcePrefab.tag; }
        catch { Log("Could not copy root tag - tag may not exist in project", LogType.Warning); }
        
        Log("Copied layers and tags", LogType.Success);
    }
    
    #endregion
    
    #region Helper Methods for Copying
    
    private Type GetTypeByName(string typeName)
    {
        // Try to find the type in all loaded assemblies
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            var type = assembly.GetType(typeName);
            if (type != null) return type;
        }
        
        // Try simple name search
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            foreach (var type in assembly.GetTypes())
            {
                if (type.Name == typeName || type.FullName == typeName)
                    return type;
            }
        }
        
        return null;
    }
    
    private void CopyComponentValues(Component source, Component target)
    {
        if (source == null || target == null) return;
        if (source.GetType() != target.GetType()) return;
        
        try
        {
            // Use EditorUtility.CopySerialized for best results
            EditorUtility.CopySerialized(source, target);
        }
        catch
        {
            // Fallback to SerializedObject approach
            try
            {
                var sourceObj = new SerializedObject(source);
                var targetObj = new SerializedObject(target);
                
                var prop = sourceObj.GetIterator();
                while (prop.NextVisible(true))
                {
                    if (prop.name == "m_Script") continue;
                    targetObj.CopyFromSerializedProperty(prop);
                }
                
                targetObj.ApplyModifiedPropertiesWithoutUndo();
            }
            catch (Exception ex)
            {
                Log($"Could not copy all values for {source.GetType().Name}: {ex.Message}", LogType.Warning);
            }
        }
    }
    
    private void SetComponentEnabled(Component comp, bool enabled)
    {
        if (comp is Behaviour behaviour)
            behaviour.enabled = enabled;
        else if (comp is Renderer renderer)
            renderer.enabled = enabled;
        else if (comp is Collider collider)
            collider.enabled = enabled;
    }
    
    private Transform FindByPath(Transform root, string path)
    {
        if (string.IsNullOrEmpty(path)) return null;
        
        string[] parts = path.Split('/');
        Transform current = root;
        
        foreach (var part in parts)
        {
            if (string.IsNullOrEmpty(part)) continue;
            
            Transform child = current.Find(part);
            if (child == null) return null;
            current = child;
        }
        
        return current == root ? null : current;
    }
    
    private Transform FindBoneByName(Transform root, string boneName)
    {
        if (string.IsNullOrEmpty(boneName)) return null;
        
        // Direct child check
        Transform direct = root.Find(boneName);
        if (direct != null) return direct;
        
        // Recursive search
        foreach (Transform child in root)
        {
            if (child.name == boneName) return child;
            
            Transform found = FindBoneByName(child, boneName);
            if (found != null) return found;
        }
        
        return null;
    }
    
    #endregion
}
