using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Clash;
using NavisworksTransport.Core;
using NavisworksTransport.Core.Animation;
using NavisworksTransport.Utils;
using NavisworksTransport.Utils.CoordinateSystem;

namespace NavisworksTransport
{
    /// <summary>
    /// Clash Detective 集成管理器
    /// 实现动态碰撞检测与Clash Detective窗口的联动
    /// </summary>
    public class ClashDetectiveIntegration
    {
        // Windows API: 设置前台窗口
        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        private static ClashDetectiveIntegration _instance;

        private DocumentClash _documentClash;
        private List<ClashResult> _currentCollisions;
        // 通道对象缓存，用于优化碰撞检测性能
        private static HashSet<ModelItem> _channelObjectsCache = null;
        private static readonly object _cacheLock = new object();

        // 几何对象列表缓存，用于避免重复获取对象列表
        // 使用 ModelItemCollection 而非 List<ModelItem> 以避免昂贵的 ToList() 转换
        // 🔥 优化：此缓存保持稳定，不包含运动物体排除逻辑
        private static ModelItemCollection _allGeometryItemsCache = null;

        // 已过滤通道对象的几何对象列表缓存（供空间索引使用）
        // 🔥 优化：查询时动态过滤运动物体，不依赖此缓存的稳定性
        private static List<ModelItem> _nonChannelGeometryItemsCache = null;

        // 🔥 移动物体引用（用于从空间索引中排除移动物体及其所有子节点）
        private static ModelItem _animatedObject = null;

        // 🔥 新增：运动物体及其后代的快速查找集合
        // 用于查询阶段动态过滤，避免重建基础缓存
        private static HashSet<ModelItem> _animatedObjectAndDescendants = new HashSet<ModelItem>();

        // 🔥 简化设计：只保留一个权威碰撞计数器
        // 预计算碰撞数作为参数传递，不维护类级别状态
        private int _clashDetectiveCollisionCount = 0;

        /// <summary>
        /// Clash Detective检测到的权威碰撞数量（去重后的实际碰撞数）
        /// </summary>
        public int ClashDetectiveCollisionCount
        {
            get { return _clashDetectiveCollisionCount; }
        }

        /// <summary>
        /// 【已弃用】预计算碰撞检测点数量现在作为参数传递，不再维护类级别状态
        /// 此属性始终返回0，请从数据库查询或使用局部变量
        /// </summary>
        [Obsolete("预计算碰撞数现在作为参数传递，不再维护类级别状态")]
        public int AnimationCollisionCount
        {
            get { return 0; }
        }

        /// <summary>
        /// 当前ClashDetective测试名称
        /// </summary>
        public string CurrentTestName
        {
            get { return _currentTestName; }
        }

        private string _currentTestName;

        /// <summary>
        /// 设置当前测试名称（用于从数据库加载历史记录时）
        /// </summary>
        public void SetCurrentTestName(string testName)
        {
            _currentTestName = testName;
            LogManager.Info($"[ClashDetectiveIntegration] 设置当前测试名称: {testName}");
        }

        // 🔥 检测取消标志
        private bool _wasLastTestCanceled = false;

        /// <summary>
        /// 上次检测是否被取消
        /// </summary>
        public bool WasLastTestCanceled
        {
            get { return _wasLastTestCanceled; }
        }

        /// <summary>
        /// 单例实例
        /// </summary>
        public static ClashDetectiveIntegration Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new ClashDetectiveIntegration();
                }
                return _instance;
            }
        }

        /// <summary>
        /// 当前检测到的碰撞结果
        /// </summary>
        public List<ClashResult> CurrentCollisions
        {
            get { return _currentCollisions ?? new List<ClashResult>(); }
        }

        /// <summary>
        /// 碰撞检测结果变化事件
        /// </summary>
        public event EventHandler<CollisionDetectedEventArgs> CollisionDetected;

        /// <summary>
        /// ClashDetective结果保存到数据库事件
        /// </summary>
        public event EventHandler<ClashDetectiveResultSavedEventArgs> ClashDetectiveResultSaved;

        private ClashDetectiveIntegration()
        {
            _currentCollisions = new List<ClashResult>();

            // 自动初始化 Clash Detective 集成
            try
            {
                LogManager.Info("初始化Clash Detective集成（.NET API模式）...");

                // 直接使用.NET API获取Clash文档
                _documentClash = Application.ActiveDocument.GetClash();
                if (_documentClash != null)
                {
                    LogManager.Info("成功获取.NET API Clash文档");
                    LogManager.Info("Clash Detective集成初始化成功（.NET API模式）");
                }
                else
                {
                    LogManager.Warning("无法获取Clash文档，Clash Detective可能未安装");
                }
            }
            catch (Exception ex)
            {
                LogManager.Error($"初始化Clash Detective集成失败: {ex.Message}");
                _documentClash = null;
            }
        }

        private List<CollisionResult> _deduplicatedCollisionResults = new List<CollisionResult>(); // 去重后的预计算碰撞结果
        private readonly object _resultsLock = new object();

        // ClashDetective测试结果缓存：pathName -> List<CollisionResult>
        // 使用自定义的CollisionResult类型，支持修改Item1和Item2
        private Dictionary<string, List<CollisionResult>> _clashDetectiveResultsCache = new Dictionary<string, List<CollisionResult>>();
        private readonly object _clashResultsCacheLock = new object();

        // 🔥 新增：记录ClashDetective确认碰撞时的位置信息
        // key: "Item1Id|Item2Id", value: 确认碰撞时的位置信息
        private Dictionary<string, CollisionPositionInfo> _confirmedCollisionPositions = new Dictionary<string, CollisionPositionInfo>();

        /// <summary>
        /// 碰撞位置信息（用于记录ClashDetective确认时的位置）
        /// </summary>
        private class CollisionPositionInfo
        {
            public Point3D AnimatedObjectTrackedPosition { get; set; }
            public Point3D Item2Position { get; set; }
            public double AnimatedObjectTrackedYawRadians { get; set; }
            public Rotation3D AnimatedObjectTrackedRotation { get; set; }
            public bool AnimatedObjectHasTrackedRotation { get; set; }
            public bool HasPositionInfo { get; set; }
        }

        /// <summary>
        /// 生成碰撞对象的唯一key（使用PathId，不使用InstanceGuid因为多数元素为0）
        /// </summary>
        private string GetCollisionObjectKey(ModelItem item1, ModelItem item2)
        {
            try
            {
                var doc = Application.ActiveDocument;
                string key1 = "null";
                string key2 = "null";
                
                if (item1 != null && doc != null)
                {
                    var pathId = doc.Models.CreatePathId(item1);
                    key1 = $"{pathId.ModelIndex}:{pathId.PathId}";
                }
                
                if (item2 != null && doc != null)
                {
                    var pathId = doc.Models.CreatePathId(item2);
                    key2 = $"{pathId.ModelIndex}:{pathId.PathId}";
                }
                
                return $"{key1}|{key2}";
            }
            catch
            {
                // 回退到DisplayName（可能不唯一，但总比空key好）
                var key1 = item1?.DisplayName ?? "null";
                var key2 = item2?.DisplayName ?? "null";
                return $"{key1}|{key2}";
            }
        }

        /// <summary>
        /// 获取去重后的预计算碰撞结果（第一次去重，按碰撞对象对去重）
        /// </summary>
        public List<CollisionResult> GetDeduplicatedCollisionResults()
        {
            lock (_resultsLock)
            {
                LogManager.Debug($"[GetDeduplicatedCollisionResults] 当前去重缓存数量: {_deduplicatedCollisionResults.Count}");
                return new List<CollisionResult>(_deduplicatedCollisionResults);
            }
        }

        /// <summary>
        /// 保存ClashDetective结果到数据库
        /// 注意：动画参数和物体配置通过 detectionRecordId 关联到 CollisionDetectionRecords 表
        /// </summary>
        private void SaveClashDetectiveResultToDatabase(string routeId, List<CollisionResult> clashResults,
                                                         int precomputedCollisionCount = 0, int? detectionRecordId = null)
        {
            try
            {
                var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
                if (pathDatabase != null && detectionRecordId.HasValue)
                {
                    // 更新碰撞检测结果到检测记录
                    pathDatabase.UpdateCollisionDetectionResult(
                        detectionRecordId.Value,
                        _currentTestName,
                        clashResults.Count,
                        precomputedCollisionCount
                    );
                    LogManager.Info($"ClashDetective结果已保存到数据库，DetectionRecordId={detectionRecordId}, TestName={_currentTestName}");

                    // 保存被撞物体信息（只保存Item2，不保存物体Item1）
                    // 同时保存碰撞时运动物体的位置和朝向，用于还原碰撞场景
                    var collisionObjects = new List<ClashDetectiveCollisionObjectRecord>();

                    foreach (var collision in clashResults)
                    {
                        // 只保存被撞到的物体（Item2）
                        if (collision.Item2 != null)
                        {
                            // 使用 CreatePathId API 获取 ModelIndex 和 PathId
                            var pathId = Application.ActiveDocument.Models.CreatePathId(collision.Item2);

                            var collisionRecord = new ClashDetectiveCollisionObjectRecord
                            {
                                DetectionRecordId = detectionRecordId.Value,
                                ModelIndex = pathId.ModelIndex,
                                PathId = pathId.PathId,
                                DisplayName = ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item2),
                                ObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item2)
                            };

                            // 保存碰撞时运动物体的位置和朝向（如果可用）
                            if (collision.HasPositionInfo && collision.AnimatedObjectTrackedPosition != null)
                            {
                                collisionRecord.AnimatedObjectTrackedPosX = collision.AnimatedObjectTrackedPosition.X;
                                collisionRecord.AnimatedObjectTrackedPosY = collision.AnimatedObjectTrackedPosition.Y;
                                collisionRecord.AnimatedObjectTrackedPosZ = collision.AnimatedObjectTrackedPosition.Z;
                                collisionRecord.AnimatedObjectTrackedYawRadians = collision.AnimatedObjectTrackedYawRadians;
                                collisionRecord.AnimatedObjectTrackedRotA = collision.AnimatedObjectTrackedRotation?.A;
                                collisionRecord.AnimatedObjectTrackedRotB = collision.AnimatedObjectTrackedRotation?.B;
                                collisionRecord.AnimatedObjectTrackedRotC = collision.AnimatedObjectTrackedRotation?.C;
                                collisionRecord.AnimatedObjectTrackedRotD = collision.AnimatedObjectTrackedRotation?.D;
                                collisionRecord.AnimatedObjectHasTrackedRotation = collision.AnimatedObjectHasTrackedRotation;
                                collisionRecord.HasPositionInfo = true;

                                LogManager.Debug($"[保存碰撞对象] 记录运动物体位置: ({collisionRecord.AnimatedObjectTrackedPosX:F2}, {collisionRecord.AnimatedObjectTrackedPosY:F2}, {collisionRecord.AnimatedObjectTrackedPosZ:F2}), yaw={collisionRecord.AnimatedObjectTrackedYawRadians:F2} rad, customRotation={collisionRecord.AnimatedObjectHasTrackedRotation}");
                            }

                            collisionObjects.Add(collisionRecord);
                        }
                    }

                    if (collisionObjects.Count > 0)
                    {
                        pathDatabase.SaveClashDetectiveCollisionObjects(detectionRecordId.Value, collisionObjects);
                        LogManager.Info($"已保存 {collisionObjects.Count} 个被撞物体到数据库");
                    }

                    // 触发结果保存事件，通知UI刷新列表（从RouteId获取PathName）
                    var pathName = GetPathNameByRouteId(routeId);
                    ClashDetectiveResultSaved?.Invoke(this, new ClashDetectiveResultSavedEventArgs(pathName, _currentTestName, clashResults.Count));
                }
            }
            catch (Exception ex)
            {
                LogManager.Error($"保存ClashDetective结果到数据库失败: {ex.Message}");
                // 不影响主流程，继续执行
            }
        }

        /// <summary>
        /// 根据RouteId获取路径名称
        /// </summary>
        private string GetPathNameByRouteId(string routeId)
        {
            if (string.IsNullOrEmpty(routeId))
                return "未知路径";

            try
            {
                var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
                if (pathDatabase != null)
                {
                    var pathRoute = pathDatabase.GetPathRouteSync(routeId);
                    return pathRoute?.Name ?? "未知路径";
                }
            }
            catch (Exception ex)
            {
                LogManager.Error($"获取路径名称失败: {ex.Message}");
            }
            return "未知路径";
        }

        /// <summary>
        /// 从数据库加载ClashDetective碰撞结果
        /// </summary>
        /// <param name="testName">测试名称</param>
        /// <returns>碰撞结果列表，如果加载失败返回null</returns>
        public List<CollisionResult> GetClashDetectiveResultsFromDatabase(string testName)
        {
            try
            {
                var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
                if (pathDatabase == null)
                {
                    LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] PathDatabase不可用");
                    return null;
                }

                // 1. 从数据库读取测试信息（直接从 CollisionDetectionRecords 表查询）
                var detectionRecord = pathDatabase.GetDetectionResultByTestName(testName);
                
                if (detectionRecord == null)
                {
                    LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 未找到测试记录: {testName}");
                    return null;
                }
                
                LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 找到测试记录: Id={detectionRecord.Id}, TestName={detectionRecord.TestName}, CollisionCount={detectionRecord.CollisionCount}");

                // 2. 重建物体对象
                ModelItem objectObject = null;
                if (detectionRecord.IsVirtualObject)
                {
                    // 创建虚拟物体（保留现有变换如果尺寸相同）
                    // 检查尺寸数据完整性
                    if (!detectionRecord.VirtualObjectLength.HasValue ||
                        !detectionRecord.VirtualObjectWidth.HasValue ||
                        !detectionRecord.VirtualObjectHeight.HasValue)
                    {
                        throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 虚拟物体记录缺少尺寸数据: TestName={testName}");
                    }
                    var modelToMeters = UnitsConverter.GetUnitsToMetersConversionFactor();
                    objectObject = VirtualObjectManager.Instance.CreateVirtualObject(
                        detectionRecord.VirtualObjectLength.Value * modelToMeters,
                        detectionRecord.VirtualObjectWidth.Value * modelToMeters,
                        detectionRecord.VirtualObjectHeight.Value * modelToMeters
                    ) ?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 获取虚拟物体失败");
                }
                else if (detectionRecord.ObjectModelIndex.HasValue && !string.IsNullOrEmpty(detectionRecord.ObjectPathId))
                {
                    // 通过 PathId 查找真实物体
                    try
                    {
                        var pathIdObj = new Autodesk.Navisworks.Api.DocumentParts.ModelItemPathId
                        {
                            ModelIndex = detectionRecord.ObjectModelIndex.Value,
                            PathId = detectionRecord.ObjectPathId
                        };
                        objectObject = Application.ActiveDocument.Models.ResolvePathId(pathIdObj) ?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法通过 PathId 找到物体对象: ModelIndex={detectionRecord.ObjectModelIndex}, PathId={detectionRecord.ObjectPathId}");
                        LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 已找到真实物体: {ModelItemAnalysisHelper.GetSafeDisplayName(objectObject)}");
                    }
                    catch (Exception ex)
                    {
                        throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] ResolvePathId 失败: ModelIndex={detectionRecord.ObjectModelIndex}, PathId={detectionRecord.ObjectPathId}", ex);
                    }
                }

                if (objectObject == null)
                {
                    throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法重建物体对象: IsVirtualObject={detectionRecord.IsVirtualObject}, ObjectModelIndex={detectionRecord.ObjectModelIndex}, ObjectPathId={detectionRecord.ObjectPathId}");
                }

                // 如果是虚拟物体且是新创建的（不是已存在的），将其移动到路径起点
                // 注意：如果虚拟物体已存在（动画刚结束），保持当前位置（终点），不要移动到起点
                if (detectionRecord.IsVirtualObject && !string.IsNullOrEmpty(detectionRecord.RouteId) && 
                    !VirtualObjectManager.Instance.IsVirtualObjectActive)
                {
                    try
                    {
                        var pathPlanningManager = PathPlanningManager.Instance;
                        var route = pathPlanningManager.GetAllRoutes().FirstOrDefault(r => r.Id == detectionRecord.RouteId);

                        if (route != null && route.Points != null && route.Points.Count > 0)
                        {
                            // 获取第一个点（起点）
                            var startPoint = route.Points.FirstOrDefault(p => p.Type == PathPointType.StartPoint) ?? route.Points[0];
                            var startPointPosition = startPoint.Position;

                            // 使用 PathAnimationManager 将物体移动到起点
                            var pathAnimationManager = PathAnimationManager.GetInstance();
                            bool moved = pathAnimationManager.MoveObjectToPathStart(objectObject, new List<Point3D> { startPointPosition, startPointPosition });
                            if (moved)
                            {
                                LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 虚拟物体已移动到路径起点: ({startPointPosition.X:F2}, {startPointPosition.Y:F2}, {startPointPosition.Z:F2})");
                            }
                            else
                            {
                                LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 虚拟物体移动到路径起点失败");
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 移动虚拟物体到起点失败: {ex.Message}");
                    }
                }
                else if (detectionRecord.IsVirtualObject && VirtualObjectManager.Instance.IsVirtualObjectActive)
                {
                    LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 虚拟物体已存在，保持当前位置（终点），不移动到起点");
                }

                // 3. 从数据库读取被撞物体信息（包含碰撞时运动物体的位置和朝向）
                var collisionObjects = pathDatabase.GetClashDetectiveCollisionObjects(detectionRecord.Id);
                LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 从数据库读取碰撞对象: DetectionRecordId={detectionRecord.Id}, 找到 {collisionObjects.Count} 个对象");

                // 4. 重建碰撞结果
                var results = new List<CollisionResult>();

                foreach (var obj in collisionObjects)
                {
                    ModelItem collidedObject = null;

                    // 优先使用 ResolvePathId API（新方式）
                    if (obj.ModelIndex.HasValue && !string.IsNullOrEmpty(obj.PathId))
                    {
                        try
                        {
                            var pathIdObj = new Autodesk.Navisworks.Api.DocumentParts.ModelItemPathId();
                            pathIdObj.ModelIndex = obj.ModelIndex.Value;
                            pathIdObj.PathId = obj.PathId;
                            collidedObject = Application.ActiveDocument.Models.ResolvePathId(pathIdObj);
                        }
                        catch (Exception ex)
                        {
                            throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] ResolvePathId 失败: ModelIndex={obj.ModelIndex}, PathId={obj.PathId}", ex);
                        }
                    }

                    if (collidedObject == null)
                    {
                        throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法通过路径找到物体对象: {obj.ModelIndex},{obj.PathId}");
                    }

                    if (!ModelItemAnalysisHelper.IsModelItemValid(collidedObject))
                    {
                        throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 找到的 ModelItem 无效: {obj.ModelIndex},{obj.PathId}");
                    }

                    // 使用重建对象的 DisplayName，而不是数据库中保存的 DisplayName
                    var collidedObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(collidedObject);

                    var collisionResult = new CollisionResult
                    {
                        ClashGuid = Guid.NewGuid(),
                        DisplayName = $"历史碰撞: {collidedObjectName}",
                        Status = ClashResultStatus.Active,
                        Item1 = objectObject,
                        Item2 = collidedObject,
                        Center = collidedObject.BoundingBox().Center,
                        Distance = 0.0,
                        CreatedTime = DateTime.Now
                    };
                    
                    // 恢复碰撞时运动物体的位置和朝向（如果可用）
                    if (obj.HasPositionInfo && obj.AnimatedObjectTrackedPosX.HasValue && obj.AnimatedObjectTrackedPosY.HasValue && obj.AnimatedObjectTrackedPosZ.HasValue)
                    {
                        collisionResult.AnimatedObjectTrackedPosition = new Point3D(obj.AnimatedObjectTrackedPosX.Value, obj.AnimatedObjectTrackedPosY.Value, obj.AnimatedObjectTrackedPosZ.Value);
                        collisionResult.AnimatedObjectTrackedYawRadians = obj.AnimatedObjectTrackedYawRadians ?? 0.0;
                        collisionResult.AnimatedObjectHasTrackedRotation = obj.AnimatedObjectHasTrackedRotation;
                        if (obj.AnimatedObjectHasTrackedRotation &&
                            obj.AnimatedObjectTrackedRotA.HasValue &&
                            obj.AnimatedObjectTrackedRotB.HasValue &&
                            obj.AnimatedObjectTrackedRotC.HasValue &&
                            obj.AnimatedObjectTrackedRotD.HasValue)
                        {
                            collisionResult.AnimatedObjectTrackedRotation = new Rotation3D(
                                obj.AnimatedObjectTrackedRotA.Value,
                                obj.AnimatedObjectTrackedRotB.Value,
                                obj.AnimatedObjectTrackedRotC.Value,
                                obj.AnimatedObjectTrackedRotD.Value);
                        }
                        collisionResult.HasPositionInfo = true;

                        LogManager.Debug($"[加载碰撞结果] 恢复运动物体位置: ({obj.AnimatedObjectTrackedPosX:F2}, {obj.AnimatedObjectTrackedPosY:F2}, {obj.AnimatedObjectTrackedPosZ:F2}), yaw={obj.AnimatedObjectTrackedYawRadians:F2} rad, customRotation={obj.AnimatedObjectHasTrackedRotation}");
                    }

                    results.Add(collisionResult);
                }

                LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 从数据库加载测试 '{testName}' 完成，重建了 {results.Count} 个碰撞结果");
                return results;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[LoadClashDetectiveResultsFromDatabase] 加载失败: {ex.Message}", ex);
                return null;
            }
        }

        /// <summary>
        /// 获取对象当前位置
        /// </summary>
        private Point3D GetObjectPosition(ModelItem item)
        {
            try
            {
                if (item == null) return new Point3D(0, 0, 0);

                var bounds = item.BoundingBox();
                if (bounds != null)
                {
                    return new Point3D(
                        (bounds.Min.X + bounds.Max.X) / 2,
                        (bounds.Min.Y + bounds.Max.Y) / 2,
                        (bounds.Min.Z + bounds.Max.Z) / 2
                    );
                }
                return new Point3D(0, 0, 0);
            }
            catch (Exception ex)
            {
                LogManager.Error($"获取对象位置失败: {ex.Message}");
                return new Point3D(0, 0, 0);
            }
        }

        /// <summary>
        /// 通过PathId向上查找匹配的原始对象
        /// 从ClashDetective返回的几何体组件开始，向上遍历父节点，
        /// 直到找到与预计算记录中任一对象PathId匹配的父节点
        /// </summary>
        private ModelItem FindMatchingObjectByPathId(ModelItem clashItem2, List<CollisionResult> precomputedCollisions)
        {
            try
            {
                if (precomputedCollisions == null || precomputedCollisions.Count == 0 || clashItem2 == null)
                    return null;

                var doc = Application.ActiveDocument;
                if (doc?.Models == null) return null;

                // 1. 收集所有预计算记录中Item2的PathId（作为HashSet提高查找效率）
                var precomputedPathIds = new HashSet<(int ModelIndex, string PathId)>();
                foreach (var precomputed in precomputedCollisions)
                {
                    if (precomputed.Item2 != null)
                    {
                        try
                        {
                            var pathId = doc.Models.CreatePathId(precomputed.Item2);
                            precomputedPathIds.Add((pathId.ModelIndex, pathId.PathId));
                        }
                        catch (Exception ex)
                        {
                            LogManager.Warning($"[PathId匹配] 获取PathId失败: {ex.Message}");
                        }
                    }
                }

                if (precomputedPathIds.Count == 0)
                {
                    LogManager.Debug($"[PathId匹配] 预计算记录中没有有效的PathId");
                    return null;
                }

                // 2. 从ClashDetective返回的Item2开始向上遍历父节点
                var current = clashItem2;
                int levels = 0;
                while (current != null && levels < 20) // 最多向上查找20层
                {
                    try
                    {
                        var currentPathId = doc.Models.CreatePathId(current);

                        // 检查PathId是否在预计算记录中
                        if (precomputedPathIds.Contains((currentPathId.ModelIndex, currentPathId.PathId)))
                        {
                            LogManager.Debug($"[PathId匹配] 成功找到匹配的父节点: {ModelItemAnalysisHelper.GetSafeDisplayName(current)} (向上{levels}层)");
                            return current;
                        }

                        // 继续向上查找
                        current = current.Parent;
                        levels++;
                    }
                    catch
                    {
                        // 如果当前节点无法获取PathId，继续向上
                        current = current.Parent;
                        levels++;
                    }
                }

                LogManager.Debug($"[PathId匹配] 未找到匹配的父节点，向上查找了{levels}层");
                return null;
            }
            catch (Exception ex)
            {
                LogManager.Warning($"[PathId匹配] 查找失败: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// 运行ClashDetective测试并保存到数据库（公共方法，供批处理和非批处理调用）
        /// </summary>
        /// <returns>碰撞分组、主测试、确认的碰撞数量、是否被取消</returns>
        public (ClashResultGroup collisionGroup, ClashTest addedMainTest, int confirmedCount, bool wasCanceled) RunClashDetectiveTestsAndSaveToDatabase(
            List<CollisionResult> precomputedCollisions,
            double detectionGap,
            string routeId,
            ModelItem animatedObject,
            bool isVirtualObject,
            int frameRate,
            double duration,
            double virtualObjectLength,
            double virtualObjectWidth,
            double virtualObjectHeight,
            Progress progress = null,
            int precomputedCollisionCount = 0,
            int? detectionRecordId = null)
        {
            LogManager.Info($"[ClashDetective] 开始运行碰撞检测并保存到数据库（容差: {detectionGap}米）");

            // 保存碰撞检测前的物体状态
            if (animatedObject != null && IsModelItemValid(animatedObject))
            {
                PathAnimationManager.GetInstance().SaveObjectState(animatedObject);
            }

            // 过滤有效的碰撞
            var validCollisions = precomputedCollisions.Where(collision =>
                collision.HasPositionInfo &&
                IsModelItemValid(collision.Item1) &&
                IsModelItemValid(collision.Item2)
            ).ToList();

            LogManager.Info($"[ClashDetective] 有效碰撞数量: {validCollisions.Count}");

            // 第一步：创建主测试（不包含选择集，纯容器）
            var mainTestName = $"碰撞检测_{DateTime.Now:MMdd_HHmmssfff}";
            _currentTestName = mainTestName;
            LogManager.Info($"[分组测试] 创建主测试: {mainTestName}");

            // 创建主测试
            var mainTest = new ClashTest
            {
                DisplayName = mainTestName,
                TestType = ClashTestType.Hard,
                Tolerance = detectionGap,
                Guid = Guid.Empty,
                MergeComposites = true
            };

            // 添加主测试到文档
            _documentClash.TestsData.TestsAddCopy(mainTest);
            LogManager.Info($"[分组测试] 主测试已添加到文档");

            // 获取添加后的测试对象引用
            var addedMainTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == mainTestName) as ClashTest;
            if (addedMainTest == null)
            {
                LogManager.Error("[分组测试] 无法获取添加后的主测试对象");
                return (null, null, 0, false);
            }

            // 如果没有有效碰撞，直接保存空记录并返回
            if (validCollisions.Count == 0)
            {
                LogManager.Warning("[ClashDetective] 没有有效的碰撞，保存空碰撞记录");

                // 重置碰撞计数器（重要：避免保留上次测试的值）
                _clashDetectiveCollisionCount = 0;

                // 保存到数据库（配置参数通过 detectionRecordId 关联获取）
                SaveClashDetectiveResultToDatabase(
                    routeId,
                    new List<CollisionResult>(),
                    precomputedCollisionCount,
                    detectionRecordId
                );

                return (null, addedMainTest, 0, false);
            }

            // 第二步：创建分组并添加碰撞结果（应用智能去重）
            // 1. 分组：按碰撞对象对分组
            var groupedCollisions = validCollisions
                .GroupBy(c => new { Item1 = c.Item1, Item2 = c.Item2 })
                .ToList();

            LogManager.Info($"[分组测试] 智能去重: {validCollisions.Count} 个检测点 -> {groupedCollisions.Count} 个唯一碰撞对");

            // 🔥 初始化确认碰撞位置字典
            _confirmedCollisionPositions.Clear();

            // 缓存去重后的碰撞结果（每组取第一个）
            lock (_resultsLock)
            {
                _deduplicatedCollisionResults.Clear();
                foreach (var group in groupedCollisions)
                {
                    _deduplicatedCollisionResults.Add(group.First());
                }
                LogManager.Debug($"[去重缓存] 已缓存 {_deduplicatedCollisionResults.Count} 个去重后的碰撞结果");
            }

            var collisionGroup = new ClashResultGroup
            {
                DisplayName = $"碰撞检测组 ({groupedCollisions.Count} 个唯一碰撞对)"
            };

            LogManager.Info($"[分组测试] 创建碰撞分组: {collisionGroup.DisplayName}");

            int confirmedCount = 0;
            int skippedCount = 0;
            int testCount = 0;  // 检测计数器，用于进度日志
            int totalTests = validCollisions.Count;  // 总检测次数（预估）
            int totalGroups = groupedCollisions.Count;  // 总组数
            var doc = Application.ActiveDocument;

            // 初始化进度条
            progress?.Update(0.0);

            // 收集所有临时测试，用于批量删除
            var tempTestsToRemove = new List<ClashTest>();

            bool wasCanceled = false;

            // 2. 遍历每一组
            for (int groupIndex = 0; groupIndex < groupedCollisions.Count; groupIndex++)
            {
                // 检查用户是否取消
                if (progress != null && progress.IsCanceled)
                {
                    LogManager.Info($"[分组测试] 用户取消操作，已处理 {groupIndex}/{groupedCollisions.Count} 组");
                    wasCanceled = true;
                    break;
                }

                // 更新进度条（每5组更新一次，避免过于频繁）
                if (groupIndex % 5 == 0 || groupIndex == groupedCollisions.Count - 1)
                {
                    double progressPercent = (double)groupIndex / totalGroups * 0.7; // 组进度占70%
                    progress?.Update(progressPercent);
                }

                var group = groupedCollisions[groupIndex];

                // 3. 排序：按距离/重叠深度排序，优先检测最严重的碰撞
                // 注意：Distance通常越小（或负值越大）表示碰撞越深，具体取决于计算方式
                // 这里假设Distance越小越严重
                var sortedCandidates = group.OrderBy(c => c.Distance).ToList();

                bool pairConfirmed = false;

                // 4. 验证即止：逐个检测候选帧
                for (int i = 0; i < sortedCandidates.Count; i++)
                {
                    var candidate = sortedCandidates[i];

                    try
                    {
                        var testAnimatedObject = candidate.Item1;
                        var modelItems = new ModelItemCollection { testAnimatedObject };
                    var targetPosition = candidate.AnimatedObjectTrackedPosition;
                    var targetYaw = candidate.AnimatedObjectTrackedYawRadians;
                    var targetRotation = candidate.AnimatedObjectTrackedRotation;

                    if (candidate.AnimatedObjectHasTrackedRotation && targetRotation != null)
                        {
                            LogManager.Info(
                                $"[ClashDetective验证] 已跟踪姿态候选恢复: " +
                                $"对象={testAnimatedObject.DisplayName}, " +
                                $"目标跟踪点=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3})");

                            var pam = PathAnimationManager.GetInstance();
                            if (pam != null && pam.ControlsAnimatedObject(testAnimatedObject))
                            {
                                pam.MoveAnimatedObjectToPose(
                                    testAnimatedObject,
                                    targetPosition,
                                    targetYaw,
                                    targetRotation,
                                    true);
                            }
                            else
                            {
                                throw new InvalidOperationException(
                                    $"ClashDetective已跟踪姿态候选恢复失败：对象 {testAnimatedObject.DisplayName} 不受当前动画管理器控制。");
                            }
                        }
                        else
                        {
                            throw new InvalidOperationException(
                                $"ClashDetective候选恢复缺少完整姿态：对象 {testAnimatedObject.DisplayName}，" +
                                $"目标跟踪点=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3})，" +
                                $"TrackedYaw={targetYaw:F6}rad。");
                        }

                        var tempTestName = $"临时验证_{confirmedCount + 1}_{i}_{DateTime.Now:HHmmss_fff}";

                        // 🔥 优化3：预配置 ClashTest，避免后续 CreateCopy 和 TestsEditTestFromCopy 调用
                        var tempTest = new ClashTest
                        {
                            DisplayName = tempTestName,
                            TestType = ClashTestType.Hard,
                            Tolerance = detectionGap,
                            Guid = Guid.Empty,
                            MergeComposites = true
                        };

                        var selectionA = new ModelItemCollection { candidate.Item1 };
                        var selectionB = new ModelItemCollection { candidate.Item2 };
                        tempTest.SelectionA.Selection.CopyFrom(selectionA);
                        tempTest.SelectionB.Selection.CopyFrom(selectionB);

                        // 直接设置 PrimitiveTypes，避免后续的 CreateCopy 和 TestsEditTestFromCopy
                        tempTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles;
                        tempTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;

                        _documentClash.TestsData.TestsAddCopy(tempTest);
                        var addedTempTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == tempTestName) as ClashTest;

                        if (addedTempTest != null)
                        {
                            // 🔥 优化3：跳过了 addedTempTest.CreateCopy() 和 TestsEditTestFromCopy 调用
                            // 因为 PrimitiveTypes 已在创建 tempTest 时设置好

                            _documentClash.TestsData.TestsRunTest(addedTempTest);

                            // 增加检测计数并打印进度日志（每50次更新一次进度条，每100次打印日志）
                            testCount++;
                            if (testCount % 50 == 0)
                            {
                                // 基于总检测次数计算进度（组进度占70%，检测进度占30%）
                                double groupProgress = (double)(groupIndex + 1) / totalGroups * 0.7;
                                double testProgress = (double)testCount / totalTests * 0.3;
                                double totalProgress = Math.Min(groupProgress + testProgress, 0.99);
                                progress?.Update(totalProgress);
                            }
                            if (testCount % 100 == 0)
                            {
                                double logProgressPercent = (double)testCount / totalTests * 100;
                                LogManager.Info($"[ClashDetective进度] 已执行 {testCount}/{totalTests} 次检测 ({logProgressPercent:F1}%)");
                            }

                            var refreshedTempTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == tempTestName) as ClashTest;
                            if (refreshedTempTest != null && refreshedTempTest.Children.Count > 0)
                            {
                                // ！！！发现真实碰撞！！！
                                pairConfirmed = true;
                                confirmedCount++;
                                skippedCount += (sortedCandidates.Count - 1 - i); // 记录跳过的数量

                                // 🔥 关键：记录确认碰撞的候选帧位置信息，供后续使用
                                // 使用Item2作为key，因为最终去重也是按Item2合并的
                                var confirmedPositionKey = GetCollisionObjectKey(candidate.Item1, candidate.Item2);
                                _confirmedCollisionPositions[confirmedPositionKey] = new CollisionPositionInfo
                                {
                        AnimatedObjectTrackedPosition = candidate.AnimatedObjectTrackedPosition,
                                    Item2Position = candidate.Item2Position,
                        AnimatedObjectTrackedYawRadians = candidate.AnimatedObjectTrackedYawRadians,
                        AnimatedObjectTrackedRotation = candidate.AnimatedObjectTrackedRotation,
                        AnimatedObjectHasTrackedRotation = candidate.AnimatedObjectHasTrackedRotation,
                                    HasPositionInfo = candidate.HasPositionInfo
                                };
                    LogManager.Debug($"[ClashDetective确认] 记录碰撞位置: {confirmedPositionKey}, 位置: ({candidate.AnimatedObjectTrackedPosition.X:F2}, {candidate.AnimatedObjectTrackedPosition.Y:F2}, {candidate.AnimatedObjectTrackedPosition.Z:F2})");

                                int subResultIndex = 1;
                                // 🔥 优化：预获取运动物体名称，避免在循环中重复获取
                                var animatedObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(animatedObject);

                                foreach (var child in refreshedTempTest.Children)
                                {
                                    if (child is ClashResult result)
                                    {
                                        var copiedResult = result.CreateCopy() as ClashResult;
                                        copiedResult.Guid = Guid.NewGuid(); // 生成新的GUID

                                        // 设置碰撞名称：运动物体名称直接使用预获取的，被撞物体查找父容器
                                        var container2 = ModelItemAnalysisHelper.FindNamedParentContainer(result.Item2);
                                        var object2Name = ModelItemAnalysisHelper.GetSafeDisplayName(container2);

                                        var timeStamp = DateTime.Now.ToString("HHmmss");
                                        copiedResult.DisplayName = $"物流碰撞#{confirmedCount:00}-{subResultIndex:00}_{timeStamp}: {animatedObjectName} ↔ {object2Name}";

                                        collisionGroup.Children.Add(copiedResult);
                                        subResultIndex++;
                                    }
                                }
                            }

                            // 🔥 优化4：收集测试用于批量删除，而不是立即删除
                            tempTestsToRemove.Add(refreshedTempTest ?? addedTempTest);

                            if (pairConfirmed)
                            {
                                break;
                            }
                        }
                    }
                    catch (Exception itemEx)
                    {
                        LogManager.Error($"[ClashDetective] 候选检测失败: {itemEx.Message}");
                    }
                }
            }

            // 如果用户取消，直接返回，不执行后续操作（临时测试将在下方统一清理）
            if (wasCanceled)
            {
                LogManager.Info($"[分组测试] 检测已被用户取消，跳过结果处理和保存");
            }

            // 🔥 优化4：批量删除所有临时测试
            if (tempTestsToRemove.Count > 0)
            {
                LogManager.Debug($"[分组测试] 批量删除 {tempTestsToRemove.Count} 个临时测试");
                foreach (var test in tempTestsToRemove)
                {
                    try
                    {
                        _documentClash.TestsData.TestsRemove(test);
                    }
                    catch (Exception ex)
                    {
                        LogManager.Debug($"[分组测试] 删除临时测试失败: {ex.Message}");
                    }
                }
            }

            // 如果用户取消，直接返回，不执行后续操作
            if (wasCanceled)
            {
                return (null, addedMainTest, confirmedCount, true);
            }

            // 完成进度条
            progress?.Update(1.0);
            
            LogManager.Info($"[分组测试] ClashDetective检测完成: 执行 {testCount} 次检测, 确认碰撞 {confirmedCount} 组, 跳过 {skippedCount} 个冗余检测点");

            // 第三步：处理碰撞结果
            // 🔥 重要：ClashDetective 返回的是几何体级别的碰撞结果
            // Item1 是移动物体的组件名（如"车轮1"、"车轮2"），需要统一为移动物体本身
            // Item2 是被撞物体，通过PathId向上查找匹配预计算中的原始对象
            var clashResults = new List<CollisionResult>();
            foreach (var child in collisionGroup.Children)
            {
                if (child is ClashResult clashResult)
                {
                    // 🔥 通过PathId向上查找匹配的原始对象
                    // 从ClashDetective返回的几何体组件开始，向上遍历父节点，
                    // 直到找到与预计算记录中任一对象PathId匹配的父节点
                    ModelItem originalItem2 = FindMatchingObjectByPathId(clashResult.Item2, precomputedCollisions);
                    if (originalItem2 == null)
                    {
                        originalItem2 = clashResult.Item2; // 回退到原始对象
                    }

                    // 🔥 从确认碰撞位置字典中获取位置信息（ClashDetective实际验证通过的帧）
                    var positionKey = GetCollisionObjectKey(animatedObject, originalItem2);
                    _confirmedCollisionPositions.TryGetValue(positionKey, out var confirmedPosition);

                    var collisionResult = new CollisionResult
                    {
                        ClashGuid = clashResult.Guid,
                        DisplayName = clashResult.DisplayName,
                        Status = clashResult.Status,
                        Item1 = animatedObject,
                        Item2 = originalItem2,
                        Center = clashResult.Center,
                        Distance = clashResult.Distance,
                        CreatedTime = DateTime.Now,
                        // 🔥 使用ClashDetective确认时的位置信息
                        AnimatedObjectTrackedPosition = confirmedPosition?.AnimatedObjectTrackedPosition,
                        Item2Position = confirmedPosition?.Item2Position,
                        AnimatedObjectTrackedYawRadians = confirmedPosition?.AnimatedObjectTrackedYawRadians ?? 0,
                        AnimatedObjectTrackedRotation = confirmedPosition?.AnimatedObjectTrackedRotation,
                        AnimatedObjectHasTrackedRotation = confirmedPosition?.AnimatedObjectHasTrackedRotation ?? false,
                        HasPositionInfo = confirmedPosition != null
                    };
                    clashResults.Add(collisionResult);
                }
            }

            // 🔥 新增：按碰撞对象对去重
            // 原因：同一个移动物体与同一个被撞物体的多个组件碰撞，需要合并为一个记录
            var finalClashResults = clashResults
                .GroupBy(c => new { Item1 = c.Item1, Item2 = c.Item2 })
                .Select(g => g.First())
                .ToList();

            // 🔥 日志：统计位置信息保留情况
                var withPositionCount = finalClashResults.Count(c => c.HasPositionInfo && c.AnimatedObjectTrackedPosition != null);
            LogManager.Info($"[最终去重] ClashDetective结果去重: {clashResults.Count} 个碰撞 -> {finalClashResults.Count} 个唯一碰撞对，其中 {withPositionCount} 个包含位置信息");

            // 🔥 优化：在去重后为结果设置详细的 DisplayName
            // 这样只对保留的结果进行名称计算，避免在子碰撞结果上浪费性能
            foreach (var result in finalClashResults)
            {
                if (result.Item1 != null && result.Item2 != null)
                {
                    var object1Name = ModelItemAnalysisHelper.GetSafeDisplayName(result.Item1);
                    var object2Name = ModelItemAnalysisHelper.GetSafeDisplayName(result.Item2);
                    result.DisplayName = $"物流碰撞: {object1Name} ↔ {object2Name}";
                }
            }

            // 缓存最终结果
            lock (_clashResultsCacheLock)
            {
                _clashDetectiveResultsCache[_currentTestName] = finalClashResults;
            }
            LogManager.Info($"[ClashDetective] 已缓存结果：{finalClashResults.Count}个碰撞，测试名称：{_currentTestName}");

            // 更新碰撞计数器
            _clashDetectiveCollisionCount = clashResults.Count;

            // 保存到数据库（使用去重后的结果，配置参数通过 detectionRecordId 关联获取）
            SaveClashDetectiveResultToDatabase(routeId, finalClashResults, precomputedCollisionCount, detectionRecordId);

            LogManager.Info($"[ClashDetective] 结果已保存到数据库");

            // 第四步：将分组添加到主测试（由调用方完成）
            return (collisionGroup, addedMainTest, confirmedCount, false);
        }

        /// <summary>
        /// 动画结束后统一创建和运行ClashDetective碰撞测试
        /// </summary>
        /// <param name="precomputedCollisions">预计算碰撞结果</param>
        /// <param name="detectionGap">检测间隙容差</param>
        /// <param name="routeId">路由ID</param>
        /// <param name="animatedObject">动画对象（如果是真实物体）</param>
        /// <param name="isVirtualObject">是否使用虚拟物体</param>
        /// <param name="frameRate">帧率</param>
        /// <param name="duration">动画时长</param>
        /// <param name="virtualObjectLength">虚拟物体长度（米）</param>
        /// <param name="virtualObjectWidth">虚拟物体宽度（米）</param>
        /// <param name="virtualObjectHeight">虚拟物体高度（米）</param>
        /// <param name="pathPoints">路径点列表（用于测试完成后恢复物体位置）</param>
        /// <summary>
        /// 创建并运行ClashDetective碰撞测试
        /// </summary>
        /// <returns>true = 检测完成，false = 用户取消</returns>
        public bool CreateAllAnimationCollisionTests(
            List<CollisionResult> precomputedCollisions,
            double detectionGap,
            string routeId,
            ModelItem animatedObject,
            bool isVirtualObject,
            int frameRate,
            double duration,
            double virtualObjectLength,
            double virtualObjectWidth,
            double virtualObjectHeight,
            List<Point3D> pathPoints = null,
            int? detectionRecordId = null)
        {
            // 重置取消标志
            _wasLastTestCanceled = false;
            
            try
            {
                LogManager.Info($"=== 使用预计算碰撞数据创建ClashDetective测试（容差: {detectionGap}米）===");

                LogManager.Info($"[预计算数据] 共有 {precomputedCollisions.Count} 个碰撞记录");
                
                // 检查预计算碰撞结果是否包含位置信息
            var collisionsWithPosition = precomputedCollisions.Count(c => c.HasPositionInfo && c.AnimatedObjectTrackedPosition != null);
                LogManager.Info($"[预计算数据] 包含位置信息的碰撞: {collisionsWithPosition}/{precomputedCollisions.Count}");

                // 直接使用所有预计算结果，只过滤有效对象（不去重）
                var collisionResults = precomputedCollisions
                    .Where(result => IsModelItemValid(result.Item1) && IsModelItemValid(result.Item2))
                    .ToList();

                // 记录预计算碰撞数量（作为参考值，不作为类状态）
                int precomputedCollisionCount = collisionResults.Count;
                LogManager.Info($"[预计算处理] 原始记录: {precomputedCollisions.Count}，有效碰撞: {precomputedCollisionCount}");

                // 获取动画对象
                if (collisionResults.Count > 0)
                {
                    animatedObject = collisionResults[0].Item1;
                }

                var doc = Application.ActiveDocument;

                // 调用公共方法运行ClashDetective测试并保存到数据库
                Progress progress = Application.BeginProgress("碰撞检测数据分析中，请稍候...");
                ClashTest addedMainTest = null;
                ClashResultGroup collisionGroup = null;
                int confirmedCount = 0;
                bool wasCanceled = false;

                try
                {
                    var result = RunClashDetectiveTestsAndSaveToDatabase(
                        precomputedCollisions,
                        detectionGap,
                        routeId,
                        animatedObject,
                        isVirtualObject,
                        frameRate,
                        duration,
                        virtualObjectLength,
                        virtualObjectWidth,
                        virtualObjectHeight,
                        progress,
                        precomputedCollisionCount,
                        detectionRecordId
                    );
                    collisionGroup = result.collisionGroup;
                    addedMainTest = result.addedMainTest;
                    confirmedCount = result.confirmedCount;
                    wasCanceled = result.wasCanceled;

                    // 检查是否被取消
                    if (wasCanceled)
                    {
                        LogManager.Info("[CreateAllAnimationCollisionTests] 用户取消了碰撞检测");
                        
                        // 设置取消标志
                        _wasLastTestCanceled = true;
                        
                        // 清空当前测试结果，避免取消后显示旧结果
                        ClearCurrentTestCache();
                        _currentTestName = null;
                        _clashDetectiveCollisionCount = 0;
                        
                        // 恢复物体状态
                        if (animatedObject != null && IsModelItemValid(animatedObject))
                        {
                            PathAnimationManager.GetInstance().RestoreAnimatedObjectState(animatedObject);
                        }
                        
                        // 重置动画完成标志，允许重新运行动画
                        ResetAnimationCompletedFlag(routeId);
                    }
                }
                finally
                {
                    // 先激活 Navisworks 主窗口（避免关闭进度条后丢失焦点）
                    try
                    {
                        var mainWindow = Autodesk.Navisworks.Api.Application.Gui.MainWindow;
                        if (mainWindow != null)
                        {
                            // 使用 Windows API 激活窗口
                            SetForegroundWindow(mainWindow.Handle);
                            LogManager.Debug("[ClashDetective] 已激活 Navisworks 主窗口");
                        }
                    }
                    catch (Exception ex)
                    {
                        LogManager.Debug($"[ClashDetective] 激活主窗口失败: {ex.Message}");
                    }
                    
                    // 关闭进度条
                    Application.EndProgress();
                }

                // 如果取消，显示提示消息后返回
                if (wasCanceled)
                {
                    // 在 finally 之后显示消息对话框
                    ShowCancellationMessage();
                    return false; // 返回取消状态
                }

                // UI操作：将分组添加到主测试
                if (collisionGroup != null && addedMainTest != null && collisionGroup.Children.Count > 0)
                {
                    _documentClash.TestsData.TestsAddCopy(addedMainTest, collisionGroup);
                }
                else
                {
                    LogManager.Warning("[分组测试] 分组为空（未检测到真实几何碰撞），未添加到主测试");
                }

                // 恢复物体到碰撞检测前的状态
                if (animatedObject != null && IsModelItemValid(animatedObject))
                {
                    PathAnimationManager.GetInstance().RestoreAnimatedObjectState(animatedObject);
                }

                // 检查是否成功创建了主测试
                var finalMainTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName.Contains("碰撞检测")) as ClashTest;
                if (finalMainTest != null)
                {
                    // 刷新Clash Detective窗口
                    RefreshClashDetectiveUI();

                    // 自动高亮ClashDetective结果
                    ModelHighlightHelper.HighlightClashDetectiveResults(_currentTestName, GetCurrentPathClashResults);
                    LogManager.Info("自动高亮ClashDetective检测结果完成");

                    LogManager.Info($"=== 碰撞统计最终结果 ===");
                    LogManager.Info($"预计算碰撞检测点: {precomputedCollisionCount}个 (参考值)");
                    LogManager.Info($"Clash Detective权威结果: {_clashDetectiveCollisionCount}个碰撞 (权威数据)");
                }

                // 🔥 无论是否有主测试，都触发碰撞检测完成事件，通知生成报告
                // 使用ClashDetective权威结果，而不是预计算结果
                // 如果权威结果为0个碰撞，传入空列表以触发祝贺对话框
                var finalCollisions = _clashDetectiveCollisionCount > 0 ? collisionResults : new List<CollisionResult>();
                LogManager.Info($"触发CollisionDetected事件，通知生成报告（{_clashDetectiveCollisionCount}个碰撞，权威数据）");
                var eventArgs = new CollisionDetectedEventArgs(finalCollisions);
                OnCollisionDetected(eventArgs);
            }
            catch (Exception ex)
            {
                LogManager.Error($"动画结束后创建测试失败: {ex.Message}");
            }
            
            return !_wasLastTestCanceled; // 返回是否成功完成（未被取消）
        }

        /// <summary>
        /// 刷新Clash Detective UI - 安全版本
        /// </summary>
        private void RefreshClashDetectiveUI()
        {
            try
            {
                // 检查文档是否有效
                var doc = Application.ActiveDocument;
                if (doc == null || doc.IsClear)
                {
                    LogManager.Warning("文档无效，跳过UI刷新");
                    return;
                }

                // 检查视图是否有效
                if (doc.ActiveView != null)
                {
                    try
                    {
                        doc.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
                        LogManager.Info("Clash Detective UI已刷新");
                    }
                    catch (ObjectDisposedException)
                    {
                        LogManager.Warning("视图对象已释放，跳过重绘");
                    }
                    catch (Exception ex)
                    {
                        LogManager.Error($"重绘视图失败: {ex.Message}");
                    }
                }
                else
                {
                    LogManager.Warning("活动视图无效，跳过UI刷新");
                }
            }
            catch (ObjectDisposedException)
            {
                LogManager.Warning("文档或视图对象已释放，跳过UI刷新");
            }
            catch (Exception ex)
            {
                LogManager.Error($"刷新Clash Detective UI失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 显示碰撞检测取消提示消息
        /// </summary>
        private void ShowCancellationMessage()
        {
            try
            {
                // 获取 Navisworks 主窗口句柄作为父窗口
                var mainWindow = System.Windows.Forms.Form.FromHandle(
                    Autodesk.Navisworks.Api.Application.Gui.MainWindow.Handle);
                
                // 使用 Windows Forms 显示提示框，指定父窗口保持焦点
                System.Windows.Forms.MessageBox.Show(
                    mainWindow,
                    "碰撞检测已取消。\n\n您可以重新运行动画来重新检测碰撞。",
                    "检测取消",
                    System.Windows.Forms.MessageBoxButtons.OK,
                    System.Windows.Forms.MessageBoxIcon.Information);
                
                LogManager.Info("[ClashDetective] 已显示取消提示消息");
            }
            catch (Exception ex)
            {
                // 如果获取父窗口失败，使用默认方式
                System.Windows.Forms.MessageBox.Show(
                    "碰撞检测已取消。\n\n您可以重新运行动画来重新检测碰撞。",
                    "检测取消",
                    System.Windows.Forms.MessageBoxButtons.OK,
                    System.Windows.Forms.MessageBoxIcon.Information);
                
                LogManager.Info("[ClashDetective] 已显示取消提示消息（无父窗口）");
            }
        }

        /// <summary>
        /// 重置动画完成标志，允许重新运行动画进行碰撞检测
        /// </summary>
        /// <param name="routeId">路径ID</param>
        private void ResetAnimationCompletedFlag(string routeId)
        {
            try
            {
                // 清除动画配置哈希到记录ID的映射，允许重新检测
                PathAnimationManager.ClearAllCollisionTestRecords();
                LogManager.Info($"[ClashDetective] 已清除检测记录映射，允许重新检测。路径ID: {routeId}");
            }
            catch (Exception ex)
            {
                LogManager.Error($"重置动画完成标志失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 检查ModelItem是否仍然有效
        /// </summary>
        private bool IsModelItemValid(ModelItem item)
        {
            try
            {
                if (item == null)
                    return false;

                // 尝试访问对象的属性来检查是否有效
                var displayName = item.DisplayName;
                var hasGeometry = item.HasGeometry;

                // 额外检查：确保对象没有被释放
                var boundingBox = item.BoundingBox();
                return true;
            }
            catch (Exception ex)
            {
                LogManager.Debug($"ModelItem无效: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 获取所有碰撞检测测试
        /// </summary>
        /// <returns>碰撞检测测试列表</returns>
        public List<ClashTest> GetClashTests()
        {
            if (_documentClash == null)
            {
                throw new InvalidOperationException("ClashDetective文档未初始化");
            }

            return _documentClash.TestsData.Tests.Cast<ClashTest>()
                .Where(t => t.DisplayName.Contains("碰撞检测"))
                .ToList();
        }

        /// <summary>
        /// 获取当前路径的Clash结果
        /// </summary>
        /// <param name="pathName">路径名称</param>
        /// <returns>当前路径的碰撞结果列表（如果没有结果返回空列表）</returns>
        public List<CollisionResult> GetCurrentPathClashResults(string testName)
        {
            lock (_clashResultsCacheLock)
            {
                // 先尝试从缓存获取
                if (_clashDetectiveResultsCache.TryGetValue(testName, out var cachedResults))
                {
                    LogManager.Debug($"[ClashDetective结果] 从缓存获取 '{testName}' 的结果：{cachedResults.Count}个碰撞");
                    return cachedResults;
                }

                // 缓存中没有，尝试从数据库加载
                LogManager.Info($"[ClashDetective结果] 缓存中没有 '{testName}'，尝试从数据库加载");
                var loadedResults = GetClashDetectiveResultsFromDatabase(testName);

                if (loadedResults != null && loadedResults.Count > 0)
                {
                    // 缓存加载的结果
                    _clashDetectiveResultsCache[testName] = loadedResults;
                    LogManager.Info($"[ClashDetective结果] 已从数据库加载 '{testName}' 的结果并缓存：{loadedResults.Count}个碰撞");
                    return loadedResults;
                }

                // 没有找到结果，返回空列表
                LogManager.Info($"[ClashDetective结果] 未找到测试 '{testName}' 的碰撞结果，返回空列表");
                return new List<CollisionResult>();
            }
        }

        /// <summary>
        /// 获取当前测试的碰撞结果（便捷方法）
        /// </summary>
        /// <returns>当前测试的碰撞结果列表</returns>
        public List<CollisionResult> GetCurrentTestResults()
        {
            return GetCurrentPathClashResults(_currentTestName);
        }

        /// <summary>
        /// 清理当前测试的缓存
        /// </summary>
        public void ClearCurrentTestCache()
        {
            if (string.IsNullOrEmpty(_currentTestName))
            {
                return;
            }

            lock (_clashResultsCacheLock)
            {
                if (_clashDetectiveResultsCache.ContainsKey(_currentTestName))
                {
                    _clashDetectiveResultsCache.Remove(_currentTestName);
                    LogManager.Info($"[ClashDetective缓存] 已清理当前测试 '{_currentTestName}' 的缓存");
                }
            }
        }

        /// <summary>
        /// 清空取消标志
        /// </summary>
        public void ClearWasLastTestCanceled()
        {
            if (_wasLastTestCanceled)
            {
                LogManager.Info("[ClashDetective] 清空取消标志");
                _wasLastTestCanceled = false;
            }
        }

        /// <summary>
        /// 清空当前测试名称
        /// </summary>
        public void ClearCurrentTestName()
        {
            if (!string.IsNullOrEmpty(_currentTestName))
            {
                LogManager.Info($"[ClashDetective] 清空当前测试名称: {_currentTestName}");
                _currentTestName = null;
            }
        }

        /// <summary>
        /// 从测试中提取碰撞结果
        /// </summary>
        /// <param name="clashTest">测试</param>
        /// <returns>碰撞结果列表</returns>
        public List<ClashResult> ExtractClashResultsFromTest(ClashTest clashTest)
        {
            var results = new List<ClashResult>();
            ExtractClashResultsFromTestRecursive(clashTest, results);
            return results;
        }

        /// <summary>
        /// 递归提取测试中的碰撞结果
        /// </summary>
        /// <param name="clashTest">测试</param>
        /// <param name="results">结果列表</param>
        private void ExtractClashResultsFromTestRecursive(ClashTest clashTest, List<ClashResult> results)
        {
            foreach (var child in clashTest.Children)
            {
                if (child is ClashResult clashResult)
                {
                    results.Add(clashResult);
                }
                else if (child is ClashResultGroup resultGroup)
                {
                    ExtractClashResultsFromGroupRecursive(resultGroup, results);
                }
            }
        }

        /// <summary>
        /// 递归提取分组中的碰撞结果
        /// </summary>
        /// <param name="group">分组</param>
        /// <param name="results">结果列表</param>
        private void ExtractClashResultsFromGroupRecursive(ClashResultGroup group, List<ClashResult> results)
        {
            foreach (var child in group.Children)
            {
                if (child is ClashResult result)
                {
                    results.Add(result);
                }
                else if (child is ClashResultGroup subGroup)
                {
                    ExtractClashResultsFromGroupRecursive(subGroup, results);
                }
            }
        }

        /// <summary>
        /// 获取所有碰撞检测的碰撞结果
        /// </summary>
        /// <returns>碰撞结果列表</returns>
        public List<ClashResult> GetAllClashResults()
        {
            var tests = GetClashTests();
            var allResults = new List<ClashResult>();

            foreach (var test in tests)
            {
                var results = ExtractClashResultsFromTest(test);
                allResults.AddRange(results);
            }

            return allResults;
        }

        /// <summary>
        /// 构建通道对象缓存，一次性扫描所有对象，避免重复的属性查询
        /// </summary>
        public void BuildChannelObjectsCache()
        {
            lock (_cacheLock)
            {
                if (_channelObjectsCache != null) return; // 双重检查锁定

                var cacheStopwatch = new System.Diagnostics.Stopwatch();
                cacheStopwatch.Start();

                _channelObjectsCache = new HashSet<ModelItem>();

                try
                {
                    var document = Application.ActiveDocument;

                    // 获取所有可通行的物流模型项
                    var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems();

                    if (allChannelItems.Count == 0)
                    {
                        LogManager.Warning("[通道缓存] ⚠️ 未找到任何可通行的物流元素，请检查模型中的物流属性设置");
                        cacheStopwatch.Stop();
                        return;
                    }

                    // 🔥 优化：只提取复合对象（IsComposite = true）
                    // 原因：通道对象通常是复合对象（如通道、楼梯、电梯）
                    // 我们只需要检测复合对象，不需要检测其子节点
                    foreach (var channelItem in allChannelItems)
                    {
                        try
                        {
                            // 只添加复合对象
                            if (channelItem.IsComposite)
                            {
                                _channelObjectsCache.Add(channelItem);
                            }
                            else if (channelItem.HasGeometry)
                            {
                                // 非复合对象但有几何数据（独立实体节点）：也添加
                                _channelObjectsCache.Add(channelItem);
                            }
                        }
                        catch (Exception ex)
                        {
                            LogManager.Warning($"[通道收集] 处理通道节点 '{channelItem?.DisplayName ?? "NULL"}' 时出错: {ex.Message}");
                            // 出错时至少保证通道本身被添加
                            _channelObjectsCache.Add(channelItem);
                        }
                    }

                    cacheStopwatch.Stop();
                    LogManager.Info($"通道对象缓存构建完成，耗时: {cacheStopwatch.ElapsedMilliseconds}ms");
                    LogManager.Info($"  - 可通行物流根对象: {allChannelItems.Count} 个");
                    LogManager.Info($"  - 缓存总对象数: {_channelObjectsCache.Count} 个");
                }
                catch (Exception ex)
                {
                    LogManager.Error($"构建通道对象缓存时发生错误: {ex.Message}", ex);
                    _channelObjectsCache = new HashSet<ModelItem>(); // 创建空缓存，避免重复构建
                }
            }
        }

        /// <summary>
        /// 清除通道对象缓存，在模型变化时调用
        /// </summary>
        public static void ClearChannelObjectsCache()
        {
            lock (_cacheLock)
            {
                _channelObjectsCache = null;
                LogManager.Debug("通道对象缓存已清除");
            }
        }

        /// <summary>
        /// 设置移动物体（用于从空间索引中排除移动物体及其所有子节点）
        /// 🔥 优化：只更新运动物体后代集合，不重建基础缓存
        /// </summary>
        /// <param name="animatedObject">移动物体</param>
        public static void SetAnimatedObject(ModelItem animatedObject)
        {
            lock (_cacheLock)
            {
                _animatedObject = animatedObject;
                LogManager.Info($"移动物体已设置: {animatedObject?.DisplayName ?? "null"}");

                // 🔥 优化：只重建运动物体后代集合，不清除基础缓存
                _animatedObjectAndDescendants.Clear();
                if (_animatedObject != null)
                {
                    var sw = System.Diagnostics.Stopwatch.StartNew();
                    CollectAllDescendants(_animatedObject, _animatedObjectAndDescendants);
                    sw.Stop();
                    LogManager.Info($"移动物体后代收集完成: {_animatedObjectAndDescendants.Count} 个对象，耗时 {sw.ElapsedMilliseconds}ms");
                }

                // 🔥 关键：只清空派生缓存，不重建基础缓存
                // 基础缓存 _allGeometryItemsCache 保持稳定
                _nonChannelGeometryItemsCache = null;
                LogManager.Info("非通道几何对象缓存已标记为失效，将在下次访问时重新过滤");
            }
        }

        /// <summary>
        /// 递归收集对象及其所有后代
        /// </summary>
        private static void CollectAllDescendants(ModelItem item, HashSet<ModelItem> collection)
        {
            if (item == null) return;

            collection.Add(item);

            // 限制递归深度，防止异常模型结构
            try
            {
                foreach (var child in item.Children)
                {
                    CollectAllDescendants(child, collection);
                }
            }
            catch (Exception ex)
            {
                LogManager.Warning($"收集对象 '{item.DisplayName}' 的后代时出错: {ex.Message}");
            }
        }

        /// <summary>
        /// 清除移动物体引用（不清除基础缓存）
        /// 🔥 优化：用于虚拟物体切换时，只清除引用而不重建80秒的基础缓存
        /// </summary>
        public static void ClearAnimatedObject()
        {
            lock (_cacheLock)
            {
                _animatedObject = null;
                _animatedObjectAndDescendants.Clear();
                LogManager.Debug("移动物体引用已清除（基础缓存保留）");
            }
        }

        /// <summary>
        /// 初始化碰撞检测缓存（用于预计算前的准备工作）
        /// 🔥 优化：基础缓存保持稳定，运动物体在查询阶段动态过滤
        /// </summary>
        /// <param name="animatedObject">移动物体</param>
        public static void InitializeCollisionDetectionCache(ModelItem animatedObject)
        {
            LogManager.Info("[碰撞检测] 开始初始化碰撞检测缓存...");

            lock (_cacheLock)
            {
                // 1. 设置运动物体（使用优化后的方法）
                _animatedObject = animatedObject;
                LogManager.Info($"移动物体已设置: {_animatedObject?.DisplayName ?? "null"}");

                // 2. 清空运动物体后代集合并重新收集
                _animatedObjectAndDescendants.Clear();
                if (_animatedObject != null)
                {
                    CollectAllDescendants(_animatedObject, _animatedObjectAndDescendants);
                    LogManager.Info($"运动物体后代收集完成: {_animatedObjectAndDescendants.Count} 个对象");
                }

                // 3. 清空派生缓存（基础缓存保持稳定）
                _nonChannelGeometryItemsCache = null;

                // 4. 如果通道缓存不存在，清空它以便重建
                if (_channelObjectsCache == null)
                {
                    LogManager.Info("通道对象缓存不存在，将重新构建");
                }
            }

            // 5. 构建基础缓存（如果不存在）- 使用双重检查锁定，不会重复构建
            BuildNonHidddenGeometryItemsCache();

            // 6. 构建通道对象缓存（如果不存在）
            if (_channelObjectsCache == null)
            {
                Instance.BuildChannelObjectsCache();
            }

            LogManager.Info("[碰撞检测] 碰撞检测缓存初始化完成");
        }

        /// <summary>
        /// 统一准备碰撞检测（整合常规处理和批处理的公共逻辑）
        /// 🔥 优化：基础缓存保持稳定，运动物体变化时快速更新
        /// </summary>
        /// <param name="animatedObject">运动物体</param>
        /// <param name="isManualMode">是否为手工指定检测对象模式</param>
        /// <param name="manualTargets">手工指定的检测目标（仅在手工模式下使用）</param>
        /// <returns>是否成功准备</returns>
        public static bool PrepareCollisionDetection(ModelItem animatedObject, bool isManualMode, List<ModelItem> manualTargets = null)
        {
            try
            {
                if (animatedObject == null)
                {
                    LogManager.Warning("[碰撞检测] 准备失败：运动物体为空");
                    return false;
                }

                if (isManualMode)
                {
                    // 手工模式：只设置移动物体，不构建全局缓存
                    LogManager.Info("[碰撞检测] 手工模式 - 跳过全局缓存初始化");
                    SetAnimatedObject(animatedObject); // 使用优化后的方法
                    return true;
                }
                else
                {
                    // 全局模式：使用优化后的初始化
                    LogManager.Info("[碰撞检测] 全局模式 - 初始化碰撞检测缓存");

                    // 🔥 优化：如果基础缓存已存在，只更新运动物体
                    if (_allGeometryItemsCache != null && _channelObjectsCache != null)
                    {
                        LogManager.Info("[碰撞检测] 基础缓存已存在，仅更新运动物体");
                        SetAnimatedObject(animatedObject);
                        return true;
                    }

                    // 否则执行完整初始化
                    InitializeCollisionDetectionCache(animatedObject);
                    return true;
                }
            }
            catch (Exception ex)
            {
                LogManager.Error($"[碰撞检测] 准备碰撞检测失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 获取通道对象缓存（供外部使用）
        /// </summary>
        /// <returns>通道对象集合，如果缓存不存在则返回null</returns>
        public static HashSet<ModelItem> GetChannelObjectsCache()
        {
            lock (_cacheLock)
            {
                return _channelObjectsCache;
            }
        }

        /// <summary>
        /// 构建几何对象列表缓存，一次性获取非隐藏几何对象
        /// </summary>
        public static void BuildNonHidddenGeometryItemsCache()
        {
            lock (_cacheLock)
            {
                if (_allGeometryItemsCache != null) return; // 双重检查锁定

                var cacheStopwatch = new System.Diagnostics.Stopwatch();
                cacheStopwatch.Start();

                try
                {
                    // 优化方案：递归遍历 (Top-Down Traversal)
                    // 原因：Search API 的 Hidden 属性不检查父级可见性，且 IsHidden 属性检查自身。
                    // 解决方案：使用堆栈进行深度优先搜索(DFS)，如果遭遇 Hidden 节点则停止深入该分支。
                    // 这天然地利用了 Navisworks 的层级可见性规则。
                    _allGeometryItemsCache = new ModelItemCollection();
                    int visibleCount = 0;

                    // 使用 Stack 模拟递归避免栈溢出，并保持对 .NET 版本的兼容性
                    // Stack 初始容量设为 1000 以减少扩容
                    var stack = new Stack<ModelItem>(1000);

                    // 将所有模型的根节点压入栈
                    foreach (var model in Application.ActiveDocument.Models)
                    {
                        stack.Push(model.RootItem);
                    }

                    while (stack.Count > 0)
                    {
                        var item = stack.Pop();

                        // 关键剪枝：如果节点显式隐藏，则跳过其整个分支（它的所有子节点都不可见）
                        if (item.IsHidden)
                            continue;

                        // 🔥 优化移除：不再在构建时排除运动物体
                        // 原因：运动物体排除移至查询阶段，通过 _animatedObjectAndDescendants 动态过滤
                        // 这样基础缓存保持稳定，切换运动物体时无需重建
                        // if (_animatedObject != null && item.Equals(_animatedObject))
                        //     continue;

                        // 🔥 优化：只提取复合对象（IsComposite = true）
                        // 原因：复合对象（如窗户）包含多个实体节点（玻璃、框架、把手）
                        // 我们只需要检测复合对象，不需要检测其子节点
                        // Clash Detective API 会自动处理复合对象的子节点几何体
                        if (item.IsComposite)
                        {
                            // 复合对象：添加到缓存，不继续遍历子节点
                            _allGeometryItemsCache.Add(item);
                            visibleCount++;
                        }
                        else if (item.HasGeometry)
                        {
                            // 非复合对象但有几何数据（独立实体节点）：添加到缓存
                            _allGeometryItemsCache.Add(item);
                            visibleCount++;
                        }
                        else
                        {
                            // 空节点：继续遍历子节点
                            foreach (var child in item.Children)
                            {
                                stack.Push(child);
                            }
                        }
                    }

                    cacheStopwatch.Stop();
                    LogManager.Info($"复合对象列表缓存构建完成 (DFS遍历 + 自动剪枝)，耗时: {cacheStopwatch.ElapsedMilliseconds}ms");
                    LogManager.Info($"  - 缓存可见对象: {visibleCount}");
                    LogManager.Info($"  - 策略: 深度优先搜索 (父节点隐藏则跳过分支)");
                }
                catch (Exception ex)
                {
                    LogManager.Error($"构建几何对象列表缓存时发生错误: {ex.Message}", ex);
                    _allGeometryItemsCache = new ModelItemCollection(); // 创建空缓存，避免重复构建
                }
            }
        }

        /// <summary>
        /// 获取复合对象缓存（供外部使用）
        /// </summary>
        /// <returns>复合对象集合，如果缓存不存在则返回null</returns>
        public static ModelItemCollection GetAllGeometryItemsCache()
        {
            lock (_cacheLock)
            {
                return _allGeometryItemsCache;
            }
        }

        /// <summary>
        /// 获取已排除通道对象的几何对象缓存（供空间索引使用）
        /// </summary>
        /// <returns>排除通道后的几何对象列表，如果缓存不存在则返回null</returns>
        public static List<ModelItem> GetNonChannelGeometryItemsCache()
        {
            lock (_cacheLock)
            {
                // 如果已经有过滤后的缓存，直接返回
                if (_nonChannelGeometryItemsCache != null)
                {
                    return _nonChannelGeometryItemsCache;
                }

                // 如果原始缓存不存在，返回 null
                if (_allGeometryItemsCache == null || _channelObjectsCache == null)
                {
                    return null;
                }

                // 第一次调用：构建并缓存过滤后的列表
                LogManager.Info($"[空间索引] 构建非通道几何对象缓存...");
                var sw = System.Diagnostics.Stopwatch.StartNew();

                // 🔥 优化：动态排除通道对象和运动物体（及其后代）
                _nonChannelGeometryItemsCache = _allGeometryItemsCache
                    .Where(item => !_channelObjectsCache.Contains(item))
                    .Where(item => !_animatedObjectAndDescendants.Contains(item))  // 🔥 动态排除运动物体
                    .ToList();

                sw.Stop();
                LogManager.Info($"[空间索引] 非通道几何对象缓存构建完成，耗时: {sw.ElapsedMilliseconds}ms，对象数: {_nonChannelGeometryItemsCache.Count}");

                return _nonChannelGeometryItemsCache;
            }
        }

        /// <summary>
        /// 清除所有缓存，在模型变化时调用
        /// </summary>
        public static void ClearAllCaches()
        {
            lock (_cacheLock)
            {
                _channelObjectsCache = null;
                _allGeometryItemsCache = null;
                _nonChannelGeometryItemsCache = null;
                _animatedObject = null; // 清除移动物体引用
                _animatedObjectAndDescendants.Clear(); // 清除运动物体后代集合
            }
        }

        /// <summary>
        /// 清理碰撞缓存
        /// </summary>
        public void ClearCollisionCache()
        {
            try
            {
                LogManager.Info("[ClashDetectiveIntegration] 清理碰撞缓存");

                // 清空当前碰撞列表
                _currentCollisions?.Clear();

                // 清空缓存的结果
                lock (_resultsLock)
                {
                    _deduplicatedCollisionResults?.Clear();
                }

                // 清除对象缓存
                ClearAllCaches();

                // 重置权威碰撞计数器（预计算计数是局部变量，无需重置）
                _clashDetectiveCollisionCount = 0;

                LogManager.Info("[ClashDetectiveIntegration] 碰撞缓存已清理");
            }
            catch (Exception ex)
            {
                LogManager.Error($"[ClashDetectiveIntegration] 清理碰撞缓存失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 清理资源
        /// </summary>
        public void Cleanup()
        {
            try
            {
                LogManager.Info("开始Clash Detective集成资源清理...");

                // 检查文档是否仍然有效，如果无效则跳过操作
                var document = Application.ActiveDocument;
                if (document == null || document.IsClear)
                {
                    LogManager.Info("文档已无效，跳过资源清理操作");
                    return;
                }

                try
                {
                    ModelHighlightHelper.ClearAllHighlights();
                    LogManager.Info("已清除临时材质高亮");
                }
                catch (Exception ex)
                {
                    LogManager.Warning($"清除临时材质失败: {ex.Message}");
                }

                // 清空内存中的结果
                _currentCollisions?.Clear();
                lock (_resultsLock)
                {
                    _deduplicatedCollisionResults?.Clear();
                }

                // 清理.NET API引用
                _documentClash = null;

                LogManager.Info("Clash Detective集成资源清理完成");
            }
            catch (Exception ex)
            {
                LogManager.Error($"清理Clash Detective资源时发生异常: {ex.Message}");
            }
        }

        /// <summary>
        /// 批处理版本：创建并运行ClashDetective测试（不触发UI操作）
        /// 此方法专门用于批处理场景，不显示进度条，不触发UI刷新
        /// </summary>
        public string CreateAllAnimationCollisionTestsForBatch(
            List<CollisionResult> precomputedCollisions,
            double detectionGap,
            string routeId,
            ModelItem animatedObject,
            bool isVirtualObject,
            int frameRate,
            double duration,
            double virtualObjectLength,
            double virtualObjectWidth,
            double virtualObjectHeight,
            List<Point3D> pathPoints = null,
            int? detectionRecordId = null)
        {
            try
            {
                LogManager.Info($"[批处理] 使用预计算碰撞数据创建ClashDetective测试（容差: {detectionGap}米）");

                LogManager.Info($"[批处理] 共有 {precomputedCollisions.Count} 个碰撞记录");

                // 记录预计算碰撞数量（作为参考值，不作为类状态）
                int precomputedCollisionCount = precomputedCollisions.Count;
                LogManager.Info($"[批处理] 预计算碰撞: {precomputedCollisionCount}");

                // 调用公共方法运行ClashDetective测试并保存到数据库
                var result = RunClashDetectiveTestsAndSaveToDatabase(
                    precomputedCollisions,
                    detectionGap,
                    routeId,
                    animatedObject,
                    isVirtualObject,
                    frameRate,
                    duration,
                    virtualObjectLength,
                    virtualObjectWidth,
                    virtualObjectHeight,
                    null,
                    precomputedCollisionCount,
                    detectionRecordId
                );

                var collisionGroup = result.collisionGroup;
                var addedMainTest = result.addedMainTest;
                var confirmedCount = result.confirmedCount;

                // 🔥 重要：更新ClashDetective碰撞计数为确认的碰撞数
                _clashDetectiveCollisionCount = confirmedCount;
                LogManager.Info($"[批处理] 更新ClashDetective碰撞计数: {confirmedCount}");

                // 添加分组到主测试（使用 TestsAddCopy 方法）
                if (collisionGroup != null && addedMainTest != null && collisionGroup.Children.Count > 0)
                {
                    _documentClash.TestsData.TestsAddCopy(addedMainTest, collisionGroup);
                    LogManager.Info($"[批处理] 碰撞分组已添加到主测试，共 {confirmedCount} 个碰撞");
                }
                else
                {
                    LogManager.Warning("[批处理] 分组为空（未检测到真实几何碰撞），未添加到主测试");
                }

                // 碰撞测试完成后，不再将物体恢复到路径起点（客户要求保持最终位置）
                // if (animatedObject != null && IsModelItemValid(animatedObject) && pathPoints != null && pathPoints.Count > 0)
                // {
                //     try
                //     {
                //         PathAnimationManager.GetInstance().MoveObjectToPathStart(animatedObject, pathPoints);
                //         LogManager.Info($"[批处理] 已将 {animatedObject.DisplayName} 恢复到路径起点位置");
                //     }
                //     catch (Exception restoreEx)
                //     {
                //         LogManager.Error($"[批处理] 恢复物体到路径起点失败: {restoreEx.Message}");
                //     }
                // }

                return addedMainTest?.DisplayName;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[批处理] 创建ClashDetective测试失败: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// 触发碰撞检测事件
        /// </summary>
        private void OnCollisionDetected(CollisionDetectedEventArgs e)
        {
            CollisionDetected?.Invoke(this, e);
        }
    }

    /// <summary>
    /// 碰撞结果数据结构
    /// </summary>
    public class CollisionResult : IEquatable<CollisionResult>
    {
        public Guid ClashGuid { get; set; }
        public string DisplayName { get; set; }
        public ClashResultStatus Status { get; set; }
        public string GridLocation { get; set; }
        public ModelItem Item1 { get; set; }
        public ModelItem Item2 { get; set; }
        public Point3D Center { get; set; }
        public double Distance { get; set; }
        public DateTime CreatedTime { get; set; }

        // 位置和朝向信息用于还原碰撞场景
        public Point3D AnimatedObjectTrackedPosition { get; set; }
        public Point3D Item2Position { get; set; }
        public double AnimatedObjectTrackedYawRadians { get; set; }  // 动画跟踪点对应的运动物体朝向（弧度）
        public Rotation3D AnimatedObjectTrackedRotation { get; set; }  // 动画跟踪点对应的运动物体完整三维姿态
        public bool AnimatedObjectHasTrackedRotation { get; set; }
        public bool HasPositionInfo { get; set; }

        // IEquatable<CollisionResult> 实现：基于碰撞对象进行去重
        public bool Equals(CollisionResult other)
        {
            if (other == null) return false;

            // 使用 ModelItem.Equals 比较底层原生对象
            bool item1Equal = (Item1 == null && other.Item1 == null) ||
                              (Item1 != null && Item1.Equals(other.Item1));
            bool item2Equal = (Item2 == null && other.Item2 == null) ||
                              (Item2 != null && Item2.Equals(other.Item2));

            return item1Equal && item2Equal;
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as CollisionResult);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int hash = 17;
                hash = hash * 31 + (Item1?.GetHashCode() ?? 0);
                hash = hash * 31 + (Item2?.GetHashCode() ?? 0);
                return hash;
            }
        }
    }

    /// <summary>
    /// 碰撞检测事件参数
    /// </summary>
    public class CollisionDetectedEventArgs : EventArgs
    {
        public List<CollisionResult> Results { get; private set; }
        public int CollisionCount { get; private set; }

        public CollisionDetectedEventArgs(List<CollisionResult> results)
        {
            Results = results;
            CollisionCount = results.Count;
        }
    }

    /// <summary>
    /// ClashDetective结果保存事件参数
    /// </summary>
    public class ClashDetectiveResultSavedEventArgs : EventArgs
    {
        public string PathName { get; private set; }
        public string TestName { get; private set; }
        public int CollisionCount { get; private set; }

        public ClashDetectiveResultSavedEventArgs(string pathName, string testName, int collisionCount)
        {
            PathName = pathName;
            TestName = testName;
            CollisionCount = collisionCount;
        }
    }
}
