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

namespace NavisworksTransport.UnitTests.Integration
{
    /// <summary>
    /// Free 自由路径集成测试（覆盖计划 T05）。
    ///
    /// Free 路径语义：任意多点，每个路径点即物体包围盒中心（轨迹即物体中心，无偏移）。
    /// 验证点：
    /// - 虚拟物体沿 Free 路径完整动画/碰撞流程
    /// - 逐帧位置精确匹配路径点（XYZ 全匹配，区别于 Ground/Hoisting 的中心高度偏移）
    /// </summary>
    [TestClass]
    [TestCategory("NavisworksIntegration")]
    public class FreePathAutomationTests
    {
        private const double Tolerance = 1e-2;
        private const int TestFrameRate = 15;
        private const double TestDurationSeconds = 5.0;

        [TestMethod]
        [Timeout(360000)]
        public async Task FreeVirtualObject_Animation_Completes()
        {
            using (var client = new NavisworksTestAutomationClient())
            {
                await client.EnsureServiceReadyAsync(TimeSpan.FromSeconds(90)).ConfigureAwait(false);

                JObject response = await client.RunCollisionTestAsync(
                    "Free",
                    240,
                    frameRate: TestFrameRate,
                    durationSeconds: TestDurationSeconds).ConfigureAwait(false);
                Assert.IsTrue(response.Value<bool>("ok"), "Free 路径碰撞测试失败: " + (string)response["error"]);

                JObject data = (JObject)response["data"];
                JObject route = (JObject)data["route"];
                Assert.IsNotNull(route, "缺少 route");
                Assert.AreEqual("Free", (string)route["pathType"], "返回的路径类型不匹配");
                Assert.AreEqual("自动测试_Free", (string)route["name"], "应使用标准 Free 测试路径");

                JObject animation = (JObject)data["animation"];
                Assert.AreEqual("Finished", (string)animation["currentState"], "动画未完成");
                Assert.IsTrue((int)animation["totalFrames"] > 0, "动画总帧数应大于 0");

                CollisionReportAssertions.AssertReportStructure(data, route);
            }
        }

        [TestMethod]
        [Timeout(240000)]
        public async Task FreeVirtualObject_FramesMatchPathPoints()
        {
            using (var client = new NavisworksTestAutomationClient())
            {
                await client.EnsureServiceReadyAsync(TimeSpan.FromSeconds(90)).ConfigureAwait(false);

                JObject response = await client.ProbeAnimationFramesAsync(
                    "Free",
                    120,
                    "0,0.5,1.0",
                    TestFrameRate,
                    TestDurationSeconds).ConfigureAwait(false);
                Assert.IsTrue(response.Value<bool>("ok"), "帧探针失败: " + (string)response["error"]);

                JObject data = (JObject)response["data"];
                Assert.AreEqual("自动测试_Free", (string)data["route"]["name"], "探针路径不匹配");

                JObject pathStart = (JObject)data["pathStart"];
                JObject pathEnd = (JObject)data["pathEnd"];
                JArray probes = (JArray)data["probes"];
                Assert.IsNotNull(probes, "缺少 probes");
                Assert.AreEqual(3, probes.Count, "探针点数应为 3（0/0.5/1.0）");

                var p0 = (JObject)probes[0]["position"];
                var pm = (JObject)probes[1]["position"];
                var p1 = (JObject)probes[2]["position"];

                // Free 路径：物体中心 = 路径点（XYZ 全匹配）
                AssertNear((double)pathStart["x"], (double)p0["x"], "起点 X");
                AssertNear((double)pathStart["y"], (double)p0["y"], "起点 Y");
                AssertNear((double)pathStart["z"], (double)p0["z"], "起点 Z");
                AssertNear((double)pathEnd["x"], (double)p1["x"], "终点 X");
                AssertNear((double)pathEnd["y"], (double)p1["y"], "终点 Y");
                AssertNear((double)pathEnd["z"], (double)p1["z"], "终点 Z");

                // 中间帧在起终点之间（3D 距离）
                double startToMid = Distance3D(p0, pm);
                double startToEnd = Distance3D(p0, p1);
                Assert.IsTrue(startToMid > Tolerance, "中间帧不应等于起点");
                Assert.IsTrue(startToMid < startToEnd - Tolerance, "中间帧应在起终点之间");

                // yaw 应为有限值（Free 路径保持物体姿态）
                foreach (var probe in probes)
                {
                    Assert.IsFalse(double.IsNaN((double)probe["yawDegrees"]), "yaw 不应为 NaN");
                }
            }
        }

        private static double Distance3D(JObject a, JObject b)
        {
            double dx = (double)a["x"] - (double)b["x"];
            double dy = (double)a["y"] - (double)b["y"];
            double dz = (double)a["z"] - (double)b["z"];
            return Math.Sqrt(dx * dx + dy * dy + dz * dz);
        }

        private static void AssertNear(double expected, double actual, string message)
        {
            Assert.IsTrue(
                Math.Abs(expected - actual) <= Tolerance,
                $"{message}：期望 {expected:F3}，实际 {actual:F3}，容差 {Tolerance}");
        }
    }
}
