using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json.Linq;

namespace NavisworksTransport.UnitTests.Integration
{
    /// <summary>
    /// 真实物体动画集成测试（覆盖计划 T01-T03，高风险区：真实物体姿态链）。
    ///
    /// 验证点：
    /// - 通过 list-model-items 动态解析真实物体（ModelPath(PathId) 跨会话不稳定，禁止硬编码）
    /// - 同名多实例时按“离路径起点最近”选择（如模型中有多个 42" Diameter 管道）
    /// - 真实物体沿三类路径生成动画并完整播放（fragment 代表姿态 / 真实尺寸）
    ///
    /// 路径起点坐标来自 resources/auto-test-routes.xml（模型坐标稳定，不随会话变化）。
    /// </summary>
    [TestClass]
    [TestCategory("NavisworksIntegration")]
    public class RealObjectAnimationAutomationTests
    {
        private const int TestFrameRate = 15;
        private const double TestDurationSeconds = 5.0;

        // 路径起点（模型单位，来自 auto-test-routes.xml）
        private static readonly (double X, double Y, double Z) GroundStart = (-216.959, 14.833, 7.698);
        private static readonly (double X, double Y, double Z) HoistingStart = (-221.750, 10.833, -14.097);
        private static readonly (double X, double Y, double Z) RailStart = (-207.591, 29.482, 3.504);

        [TestMethod]
        [Timeout(360000)]
        public async Task GroundRealObject_Animation_Completes()
        {
            await AssertRealObjectFlowAsync("Ground", "Chair Lounge Couch Double", GroundStart).ConfigureAwait(false);
        }

        [TestMethod]
        [Timeout(360000)]
        public async Task HoistingRealObject_Animation_Completes()
        {
            await AssertRealObjectFlowAsync("Hoisting", "25\" x 25\"", HoistingStart).ConfigureAwait(false);
        }

        [TestMethod]
        [Timeout(360000)]
        public async Task RailRealObject_Animation_Completes()
        {
            await AssertRealObjectFlowAsync("Rail", "42\" Diameter", RailStart).ConfigureAwait(false);
        }

        private static async Task AssertRealObjectFlowAsync(
            string pathType,
            string objectDisplayName,
            (double X, double Y, double Z) routeStart)
        {
            using (var client = new NavisworksTestAutomationClient())
            {
                await client.EnsureServiceReadyAsync(TimeSpan.FromSeconds(90)).ConfigureAwait(false);

                // 动态解析真实物体 PathId（离路径起点最近的同名实例）
                string animatedObjectPath = await ResolveRealObjectPathIdAsync(
                    client, objectDisplayName, routeStart).ConfigureAwait(false);

                JObject response = await client.RunCollisionTestAsync(
                    pathType,
                    240,
                    useVirtualObject: false,
                    animatedObjectPath: animatedObjectPath,
                    frameRate: TestFrameRate,
                    durationSeconds: TestDurationSeconds).ConfigureAwait(false);

                Assert.IsTrue(response.Value<bool>("ok"), "真实物体碰撞测试 HTTP 接口返回失败: " + (string)response["error"]);

                JObject data = (JObject)response["data"];
                Assert.IsNotNull(data, "缺少测试结果 data");

                JObject animatedObject = (JObject)data["animatedObject"];
                Assert.IsNotNull(animatedObject, "缺少 animatedObject");
                Assert.AreEqual("RealObject", (string)animatedObject["mode"], "当前测试应使用真实物体模式");
                Assert.IsFalse(string.IsNullOrWhiteSpace((string)animatedObject["displayName"]), "真实物体显示名为空");

                JObject route = (JObject)data["route"];
                Assert.IsNotNull(route, "缺少 route");
                Assert.AreEqual(pathType, (string)route["pathType"], "返回的路径类型不匹配");

                JObject animation = (JObject)data["animation"];
                Assert.IsNotNull(animation, "缺少 animation");
                Assert.AreEqual("Finished", (string)animation["currentState"], "动画未完成");
                Assert.IsTrue((int)animation["totalFrames"] > 0, "动画总帧数应大于 0");
                Assert.IsTrue(animation["detectionRecordId"] != null && (int)animation["detectionRecordId"] > 0, "检测记录 ID 无效");

                CollisionReportAssertions.AssertReportStructure(data, route);
            }
        }

        /// <summary>
        /// 枚举同名物体并选择离参考点（路径起点）最近的实例，返回其 PathId。
        /// ModelPath 跨会话不稳定，必须动态解析而非硬编码。
        /// </summary>
        private static async Task<string> ResolveRealObjectPathIdAsync(
            NavisworksTestAutomationClient client,
            string displayName,
            (double X, double Y, double Z) referencePoint)
        {
            JObject response = await client.ListModelItemsAsync(displayName, 100).ConfigureAwait(false);
            Assert.IsTrue(response.Value<bool>("ok"), "枚举模型物体失败: " + (string)response["error"]);

            JObject data = (JObject)response["data"];
            JArray items = data["items"] as JArray;
            Assert.IsNotNull(items, "缺少 items");
            Assert.IsTrue(items.Count > 0, $"未找到物体: {displayName}");

            string bestPathId = null;
            double bestDistanceSq = double.MaxValue;
            foreach (JToken token in items)
            {
                JObject item = (JObject)token;
                JObject center = (JObject)item["boundingBoxCenter"];
                string pathId = (string)item["pathId"];
                if (center == null || string.IsNullOrWhiteSpace(pathId))
                {
                    continue;
                }

                double dx = (double)center["x"] - referencePoint.X;
                double dy = (double)center["y"] - referencePoint.Y;
                double dz = (double)center["z"] - referencePoint.Z;
                double distanceSq = dx * dx + dy * dy + dz * dz;

                if (distanceSq < bestDistanceSq)
                {
                    bestDistanceSq = distanceSq;
                    bestPathId = pathId;
                }
            }

            Assert.IsNotNull(bestPathId, $"无法定位物体（无包围盒中心）: {displayName}");
            return bestPathId;
        }
    }
}
