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

namespace NavisworksTransport.UnitTests.Integration
{
    internal sealed class NavisworksTestAutomationClient : IDisposable
    {
        private readonly HttpClient _httpClient;

        public NavisworksTestAutomationClient()
        {
            _httpClient = new HttpClient
            {
                BaseAddress = new Uri("http://127.0.0.1:18777"),
                // 全局不设超时：每个请求用 CancellationToken 单独控制（动画/碰撞类请求可能远大于 20s）
                Timeout = Timeout.InfiniteTimeSpan
            };
        }

        public async Task EnsureServiceReadyAsync(TimeSpan timeout)
        {
            DateTime deadlineUtc = DateTime.UtcNow.Add(timeout);
            Exception lastError = null;

            while (DateTime.UtcNow < deadlineUtc)
            {
                try
                {
                    JObject pingResponse = await GetJsonAsync("/api/test/ping").ConfigureAwait(false);
                    if (pingResponse.Value<bool?>("ok") == true)
                    {
                        return;
                    }
                }
                catch (Exception ex)
                {
                    lastError = ex;
                }

                await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
            }

            Assert.Fail(
                "Navisworks 测试服务未就绪。请先用 start-navisworks.bat 启动 Navisworks，并确保插件面板已加载。最后错误: {0}",
                lastError?.Message ?? "unknown");
        }

        public async Task<JObject> RunVirtualCollisionTestAsync(string pathType, int timeoutSeconds)
        {
            return await RunVirtualCollisionTestCoreAsync(null, pathType, timeoutSeconds).ConfigureAwait(false);
        }

        public async Task<JObject> RunVirtualCollisionTestAsync(string routeName, string pathType, int timeoutSeconds)
        {
            return await RunVirtualCollisionTestCoreAsync(routeName, pathType, timeoutSeconds).ConfigureAwait(false);
        }

        /// <summary>
        /// 运行碰撞测试（虚拟或真实物体）。
        /// useVirtualObject=false 时必须提供 animatedObjectPath（ModelPath 定位，格式如 0/0/769/0）。
        /// frameRate/durationSeconds 可覆盖动画参数加速测试（如 15 FPS / 5 秒）。
        /// </summary>
        public async Task<JObject> RunCollisionTestAsync(
            string pathType,
            int timeoutSeconds,
            string routeName = null,
            bool useVirtualObject = true,
            string animatedObjectPath = null,
            int? frameRate = null,
            double? durationSeconds = null)
        {
            var queryParts = new System.Collections.Generic.List<string>
            {
                "pathType=" + Uri.EscapeDataString(pathType),
                "timeoutSeconds=" + timeoutSeconds.ToString(CultureInfo.InvariantCulture),
                "useVirtualObject=" + (useVirtualObject ? "true" : "false")
            };

            if (!string.IsNullOrWhiteSpace(routeName))
            {
                queryParts.Add("routeName=" + Uri.EscapeDataString(routeName));
            }

            if (!string.IsNullOrWhiteSpace(animatedObjectPath))
            {
                queryParts.Add("animatedObjectPath=" + Uri.EscapeDataString(animatedObjectPath));
            }

            if (frameRate.HasValue)
            {
                queryParts.Add("frameRate=" + frameRate.Value.ToString(CultureInfo.InvariantCulture));
            }

            if (durationSeconds.HasValue)
            {
                queryParts.Add("durationSeconds=" + durationSeconds.Value.ToString(CultureInfo.InvariantCulture));
            }

            string requestUri = "/api/test/run-virtual-collision-test?" + string.Join("&", queryParts);
            return await PostJsonAsync(requestUri, timeoutSeconds + 30).ConfigureAwait(false);
        }

        private async Task<JObject> RunVirtualCollisionTestCoreAsync(string routeName, string pathType, int timeoutSeconds)
        {
            string requestUri = string.Format(
                CultureInfo.InvariantCulture,
                "/api/test/run-virtual-collision-test?routeName={0}&pathType={1}&timeoutSeconds={2}",
                Uri.EscapeDataString(routeName ?? string.Empty),
                Uri.EscapeDataString(pathType),
                timeoutSeconds);

            return await PostJsonAsync(requestUri).ConfigureAwait(false);
        }

        public async Task<JObject> ImportRouteFileAsync(string filePath, bool overwrite = false)
        {
            string requestUri = string.Format(
                "/api/test/import-route-file?path={0}&overwrite={1}",
                Uri.EscapeDataString(filePath),
                overwrite ? "true" : "false");

            return await PostJsonAsync(requestUri).ConfigureAwait(false);
        }

        public async Task<JObject> SelectRouteAsync(string routeName)
        {
            string requestUri = string.Format(
                "/api/test/select-route?name={0}",
                Uri.EscapeDataString(routeName));

            return await PostJsonAsync(requestUri).ConfigureAwait(false);
        }

        public async Task<JObject> AnalyzeAutoPathGridAsync(string pathType)
        {
            string requestUri = string.Format(
                "/api/test/analyze-auto-path-grid?pathType={0}",
                Uri.EscapeDataString(pathType));

            return await PostJsonAsync(requestUri).ConfigureAwait(false);
        }

        public async Task<JObject> RunAutoPathAsync(
            string pathType,
            string routeName,
            double objectLengthInMeters,
            double objectWidthInMeters,
            double safetyMarginInMeters,
            double gridSizeInMeters,
            string strategy = null)
        {
            string requestUri = string.Format(
                CultureInfo.InvariantCulture,
                "/api/test/run-auto-path?pathType={0}&routeName={1}&objectLengthInMeters={2}&objectWidthInMeters={3}&safetyMarginInMeters={4}&gridSizeInMeters={5}",
                Uri.EscapeDataString(pathType),
                Uri.EscapeDataString(routeName),
                objectLengthInMeters,
                objectWidthInMeters,
                safetyMarginInMeters,
                gridSizeInMeters);

            if (!string.IsNullOrWhiteSpace(strategy))
            {
                requestUri += "&strategy=" + Uri.EscapeDataString(strategy);
            }

            return await PostJsonAsync(requestUri, 120).ConfigureAwait(false);
        }

        /// <summary>
        /// 获取路径网格诊断（网格参数/路径点所在网格可通行性/段诊断）。
        /// </summary>
        public async Task<JObject> GetRouteGridDiagnosticsAsync(string routeName = null)
        {
            string requestUri = "/api/test/route-grid-diagnostics";
            if (!string.IsNullOrWhiteSpace(routeName))
            {
                requestUri += "?routeName=" + Uri.EscapeDataString(routeName);
            }

            return await GetJsonAsync(requestUri, 60).ConfigureAwait(false);
        }

        /// <summary>
        /// 查询碰撞检测记录（验证持久化：动画参数/关联路径正确落库）。
        /// </summary>
        public async Task<JObject> GetDetectionRecordAsync(int id)
        {
            string requestUri = "/api/test/detection-record?id=" + id.ToString(CultureInfo.InvariantCulture);
            return await GetJsonAsync(requestUri, 30).ConfigureAwait(false);
        }

        /// <summary>
        /// 导出路径到文件（xml/json），返回导出文件路径。
        /// </summary>
        public async Task<JObject> ExportRouteFileAsync(string routeName, string format, string exportPath)
        {
            string requestUri = string.Format(
                "/api/test/export-route-file?routeName={0}&format={1}&path={2}",
                Uri.EscapeDataString(routeName),
                Uri.EscapeDataString(format),
                Uri.EscapeDataString(exportPath));

            return await PostJsonAsync(requestUri, 60).ConfigureAwait(false);
        }

        /// <summary>
        /// 动画播放控制序列（prepare→start→pause 验证→resume→finished）。
        /// </summary>
        public async Task<JObject> AnimationPlaybackControlAsync(
            string pathType,
            int timeoutSeconds,
            int? frameRate = null,
            double? durationSeconds = null)
        {
            string requestUri = "/api/test/animation-playback-control?pathType=" + Uri.EscapeDataString(pathType)
                                + "&timeoutSeconds=" + timeoutSeconds.ToString(CultureInfo.InvariantCulture);

            if (frameRate.HasValue)
            {
                requestUri += "&frameRate=" + frameRate.Value.ToString(CultureInfo.InvariantCulture);
            }

            if (durationSeconds.HasValue)
            {
                requestUri += "&durationSeconds=" + durationSeconds.Value.ToString(CultureInfo.InvariantCulture);
            }

            return await PostJsonAsync(requestUri, timeoutSeconds + 30).ConfigureAwait(false);
        }

        /// <summary>
        /// 路径分析（效率/安全评分）。
        /// </summary>
        public async Task<JObject> AnalyzePathAsync(string routeName, string strategy = null)
        {
            string requestUri = "/api/test/analyze-path?routeName=" + Uri.EscapeDataString(routeName);
            if (!string.IsNullOrWhiteSpace(strategy))
            {
                requestUri += "&strategy=" + Uri.EscapeDataString(strategy);
            }

            return await PostJsonAsync(requestUri, 120).ConfigureAwait(false);
        }

        /// <summary>
        /// 批量任务队列添加（不执行队列处理）。
        /// </summary>
        public async Task<JObject> BatchQueueAddAsync(string routeName, int? frameRate = null, double? durationSeconds = null)
        {
            string requestUri = "/api/test/batch-queue-add?routeName=" + Uri.EscapeDataString(routeName);
            if (frameRate.HasValue)
            {
                requestUri += "&frameRate=" + frameRate.Value.ToString(CultureInfo.InvariantCulture);
            }

            if (durationSeconds.HasValue)
            {
                requestUri += "&durationSeconds=" + durationSeconds.Value.ToString(CultureInfo.InvariantCulture);
            }

            return await PostJsonAsync(requestUri, 60).ConfigureAwait(false);
        }

        /// <summary>
        /// 路径编辑（remove-point/update-point/orthogonalize/remove-loops）。
        /// </summary>
        public async Task<JObject> EditRouteAsync(
            string action,
            string routeName,
            int? pointIndex = null,
            double? x = null,
            double? y = null,
            double? z = null)
        {
            string requestUri = "/api/test/edit-route?action=" + Uri.EscapeDataString(action)
                                + "&routeName=" + Uri.EscapeDataString(routeName);

            if (pointIndex.HasValue)
            {
                requestUri += "&pointIndex=" + pointIndex.Value.ToString(CultureInfo.InvariantCulture);
            }

            if (x.HasValue) requestUri += "&x=" + x.Value.ToString(CultureInfo.InvariantCulture);
            if (y.HasValue) requestUri += "&y=" + y.Value.ToString(CultureInfo.InvariantCulture);
            if (z.HasValue) requestUri += "&z=" + z.Value.ToString(CultureInfo.InvariantCulture);

            return await PostJsonAsync(requestUri, 60).ConfigureAwait(false);
        }

        /// <summary>
        /// 剖面盒导出（指定包围盒模式），返回导出文件信息。
        /// </summary>
        public async Task<JObject> ExportSectionBoxAsync(
            string filePath,
            double x1, double y1, double z1,
            double x2, double y2, double z2)
        {
            string requestUri = string.Format(
                CultureInfo.InvariantCulture,
                "/api/test/export-section-box?mode=bbox&x1={0}&y1={1}&z1={2}&x2={3}&y2={4}&z2={5}&path={6}",
                x1, y1, z1, x2, y2, z2,
                Uri.EscapeDataString(filePath));

            return await PostJsonAsync(requestUri, 120).ConfigureAwait(false);
        }

        private async Task<JObject> GetJsonAsync(string requestUri, int timeoutSeconds = 30)
        {
            using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
            using (HttpResponseMessage response = await _httpClient.GetAsync(requestUri, cts.Token).ConfigureAwait(false))
            {
                string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                response.EnsureSuccessStatusCode();
                return JObject.Parse(content);
            }
        }

        private async Task<JObject> PostJsonAsync(string requestUri, int timeoutSeconds = 60)
        {
            using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
            using (HttpResponseMessage response = await _httpClient
                .PostAsync(requestUri, new StringContent(string.Empty), cts.Token)
                .ConfigureAwait(false))
            {
                string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                response.EnsureSuccessStatusCode();
                return JObject.Parse(content);
            }
        }

        /// <summary>
        /// 动画逐帧状态探针：生成动画后 seek 到指定进度点，返回各点物体跟踪位置/yaw。
        /// frameRate/durationSeconds 可覆盖动画参数加速测试。
        /// </summary>
        public async Task<JObject> ProbeAnimationFramesAsync(
            string pathType,
            int timeoutSeconds,
            string probeProgress = "0,0.25,0.5,0.75,1.0",
            int? frameRate = null,
            double? durationSeconds = null)
        {
            string requestUri = string.Format(
                CultureInfo.InvariantCulture,
                "/api/test/probe-animation-frames?pathType={0}&probeProgress={1}&timeoutSeconds={2}",
                Uri.EscapeDataString(pathType),
                Uri.EscapeDataString(probeProgress),
                timeoutSeconds);

            if (frameRate.HasValue)
            {
                requestUri += "&frameRate=" + frameRate.Value.ToString(CultureInfo.InvariantCulture);
            }

            if (durationSeconds.HasValue)
            {
                requestUri += "&durationSeconds=" + durationSeconds.Value.ToString(CultureInfo.InvariantCulture);
            }

            return await PostJsonAsync(requestUri, timeoutSeconds + 30).ConfigureAwait(false);
        }

        /// <summary>
        /// 枚举模型物体（按名称过滤），返回 DisplayName/PathId/包围盒中心/祖先链。
        /// </summary>
        public async Task<JObject> ListModelItemsAsync(string name = null, int maxResults = 200)
        {
            string requestUri = "/api/test/list-model-items?maxResults=" + maxResults.ToString(CultureInfo.InvariantCulture);
            if (!string.IsNullOrWhiteSpace(name))
            {
                requestUri += "&name=" + Uri.EscapeDataString(name);
            }

            return await GetJsonAsync(requestUri).ConfigureAwait(false);
        }

        public void Dispose()
        {
            _httpClient.Dispose();
        }
    }
}
