using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace NavisworksTransport.Core.Services
{
    /// <summary>
    /// Navisworks 副实例句柄：单个副实例的进程 + 端口 + HTTP 端点。
    /// 使用完必须 Dispose（关闭进程并释放端口）。
    /// </summary>
    public sealed class NavisworksInstanceHandle : IDisposable
    {
        private readonly NavisworksInstanceManager _manager;
        private Process _process;
        private bool _disposed;

        internal NavisworksInstanceHandle(NavisworksInstanceManager manager, int port, Process process)
        {
            _manager = manager;
            Port = port;
            _process = process;
        }

        public int Port { get; }

        public string BaseUrl => $"http://127.0.0.1:{Port}";

        public Process Process => _process;

        /// <summary>
        /// 副实例进程是否存活。
        /// </summary>
        public bool IsRunning => _process != null && !_process.HasExited;

        /// <summary>
        /// 副实例 HTTP 服务是否就绪（ping + 路径数据库就绪）。
        /// </summary>
        public async Task<bool> WaitReadyAsync(TimeSpan timeout)
        {
            using (var client = CreateClient())
            {
                DateTime deadline = DateTime.UtcNow.Add(timeout);
                while (DateTime.UtcNow < deadline)
                {
                    if (!IsRunning)
                    {
                        LogManager.Warning($"[实例管理] 副实例进程退出（exit={_process?.ExitCode}），就绪等待中止");
                        return false;
                    }

                    try
                    {
                        using (var response = await client.GetAsync("/api/test/status").ConfigureAwait(false))
                        {
                            if (response.IsSuccessStatusCode)
                            {
                                string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                                JObject payload = JObject.Parse(content);
                                if (payload.Value<bool?>("ok") == true &&
                                    payload["data"]?["pathManager"]?["isDatabaseReady"]?.Value<bool?>() == true)
                                {
                                    return true;
                                }
                            }
                        }
                    }
                    catch
                    {
                    }

                    await Task.Delay(1000).ConfigureAwait(false);
                }

                return false;
            }
        }

        public HttpClient CreateClient(int timeoutSeconds = 300)
        {
            return new HttpClient
            {
                BaseAddress = new Uri(BaseUrl),
                Timeout = TimeSpan.FromSeconds(timeoutSeconds)
            };
        }

        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;
            _manager.CloseInstance(this);
        }
    }

    /// <summary>
    /// Navisworks 多实例管理器（独立模块）。
    ///
    /// 职责：
    /// - 端口池分配（默认 18778 起，多实例并行）
    /// - 启动副 Navisworks 实例（注入端口/临时库环境变量，定位新进程）
    /// - 关闭/清理实例（优雅退出 → 强杀 → 释放端口）
    /// - 就绪等待（ping + 路径数据库 + 进程存活监控）
    ///
    /// 设计：独立于批处理，任何模块（批处理/映射/未来功能）都可调用。
    /// 使用约束：实例句柄必须 Dispose（using/using var），否则泄漏端口与进程。
    /// </summary>
    public sealed class NavisworksInstanceManager
    {
        public const int PortPoolStart = 18778;
        public const int PortPoolSize = 20; // 最多 20 个并行副实例（18778-18797）

        private const string PortEnvironmentVariable = "TRANSPORTPLUGIN_TEST_PORT";
        private const string DbPathEnvironmentVariable = "TRANSPORTPLUGIN_TEST_DB_PATH";
        private const string CoordinateSystemEnvironmentVariable = "TRANSPORTPLUGIN_TEST_COORD_SYSTEM";

        private static readonly Lazy<NavisworksInstanceManager> _instance =
            new Lazy<NavisworksInstanceManager>(() => new NavisworksInstanceManager());

        private readonly object _lock = new object();
        private readonly object _launchLock = new object(); // 启动+定位串行（并发快照竞争会导致 PID 错位）
        private readonly HashSet<int> _allocatedPorts = new HashSet<int>();
        private readonly HashSet<NavisworksInstanceHandle> _handles = new HashSet<NavisworksInstanceHandle>();

        private NavisworksInstanceManager()
        {
        }

        public static NavisworksInstanceManager Instance => _instance.Value;

        /// <summary>
        /// 当前管理的存活实例数。
        /// </summary>
        public int ActiveInstanceCount
        {
            get
            {
                lock (_lock)
                {
                    return _handles.Count;
                }
            }
        }

        /// <summary>
        /// 启动副 Navisworks 实例并打开模型。
        /// </summary>
        /// <param name="modelPath">副实例打开的模型文件路径（NWD）</param>
        /// <param name="dbPath">副实例临时数据库路径（null 时用模型同名 .db 推断）</param>
        /// <returns>实例句柄（未就绪，需 WaitReadyAsync）；失败返回 null</returns>
        public NavisworksInstanceHandle LaunchInstance(string modelPath, string dbPath = null)
        {
            // 清理 AutoSave 残留：NW 启动检测到会自动保存文件会弹恢复框拦截文件加载（多实例同样受影响）
            ClearNavisworksAutoSave();

            // 启动+进程定位串行：并发时 existingPids 快照竞争会导致多个句柄定位到同一进程
            lock (_launchLock)
            {
                int port = AllocatePort();
                if (port < 0)
                {
                    LogManager.Error("[实例管理] 端口池已满，无法启动更多副实例");
                    return null;
                }

                // 记录启动前已存在的 Roamer PID，用于定位新实例
                var existingPids = new HashSet<int>(Process.GetProcessesByName("Roamer").Select(p => p.Id));

                string roamerPath = ResolveRoamerPath();
                string arguments = $"/c set {PortEnvironmentVariable}={port}";
                if (!string.IsNullOrWhiteSpace(dbPath))
                {
                    arguments += $"&& set {DbPathEnvironmentVariable}={dbPath}";
                }

                // 注入主 NW 坐标系（副实例极小导出模型 AutoDetect 不稳定，Rail 姿态链必须正确 YUp/ZUp）
                var hostCoord = Utils.CoordinateSystem.CoordinateSystemManager.Instance.ResolvedType;
                string coordText = hostCoord == Utils.CoordinateSystem.CoordinateSystemType.YUp ? "YUp" : "ZUp";
                arguments += $"&& set {CoordinateSystemEnvironmentVariable}={coordText}";

                arguments += $"&& start \"\" \"{roamerPath}\" \"{modelPath}\"";

                var psi = new ProcessStartInfo
                {
                    FileName = "cmd.exe",
                    Arguments = arguments,
                    UseShellExecute = false,
                    CreateNoWindow = true
                };

                LogManager.Info($"[实例管理] 启动副实例: {roamerPath} {modelPath}, 端口={port}, db={dbPath ?? "(默认)"}");
                try
                {
                    Process.Start(psi);
                }
                catch (Exception ex)
                {
                    LogManager.Error($"[实例管理] 启动副实例失败: {ex.Message}");
                    ReleasePort(port);
                    return null;
                }

                // 定位新启动的 Roamer 进程（cmd start 是 detach，无法直接从 cmd 句柄拿到）
                Process newProcess = null;
                for (int i = 0; i < 30; i++)
                {
                    Thread.Sleep(1000);
                    newProcess = Process.GetProcessesByName("Roamer")
                        .FirstOrDefault(p => !existingPids.Contains(p.Id));
                    if (newProcess != null)
                    {
                        break;
                    }
                }

                if (newProcess == null)
                {
                    LogManager.Warning($"[实例管理] 未定位到副实例 Roamer 进程（30s 超时），端口 {port} 释放");
                    ReleasePort(port);
                    return null;
                }

                var handle = new NavisworksInstanceHandle(this, port, newProcess);
                lock (_lock)
                {
                    _handles.Add(handle);
                }

                LogManager.Info($"[实例管理] 副实例已定位: PID={newProcess.Id}, 端口={port}");
                return handle;
            }
        }

        /// <summary>
        /// 关闭实例：优雅退出 → 强杀 → 等待退出（释放模型文件句柄）→ 释放端口。
        /// </summary>
        internal void CloseInstance(NavisworksInstanceHandle handle)
        {
            Process process = handle?.Process;
            if (process != null)
            {
                try
                {
                    if (!process.HasExited)
                    {
                        process.CloseMainWindow();
                        if (!process.WaitForExit(10000))
                        {
                            process.Kill();
                            LogManager.Warning($"[实例管理] 副实例未优雅退出，已强制关闭（端口 {handle.Port}）");
                        }
                    }

                    // 确保进程完全退出（释放模型文件句柄，供调用方清理工作目录）
                    if (!process.HasExited)
                    {
                        process.WaitForExit(10000);
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Warning($"[实例管理] 关闭副实例失败: {ex.Message}");
                    try { process.Kill(); } catch { }
                    try { process.WaitForExit(5000); } catch { }
                }
            }

            lock (_lock)
            {
                _handles.Remove(handle);
            }
            ReleasePort(handle.Port);

            LogManager.Info($"[实例管理] 副实例已关闭并释放端口: {handle.Port}");
        }

        /// <summary>
        /// 关闭所有受管副实例。
        /// </summary>
        public void CloseAll()
        {
            List<NavisworksInstanceHandle> handles;
            lock (_lock)
            {
                handles = _handles.ToList();
            }

            foreach (var handle in handles)
            {
                handle.Dispose();
            }
        }

        /// <summary>
        /// 清理 NW AutoSave 文件（避免副实例启动时弹“是否打开之前保存的模型”恢复框拦截文件加载）。
        /// </summary>
        private static void ClearNavisworksAutoSave()
        {
            try
            {
                string autoSaveDir = Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                    "Autodesk", "Navisworks Manage 2026", "AutoSave");
                if (Directory.Exists(autoSaveDir))
                {
                    foreach (string file in Directory.GetFiles(autoSaveDir))
                    {
                        try
                        {
                            File.Delete(file);
                        }
                        catch
                        {
                        }
                    }
                }
            }
            catch
            {
            }
        }

        private int AllocatePort()
        {
            lock (_lock)
            {
                for (int i = 0; i < PortPoolSize; i++)
                {
                    int candidate = PortPoolStart + i;
                    if (!_allocatedPorts.Contains(candidate))
                    {
                        _allocatedPorts.Add(candidate);
                        return candidate;
                    }
                }

                return -1;
            }
        }

        private void ReleasePort(int port)
        {
            lock (_lock)
            {
                _allocatedPorts.Remove(port);
            }
        }

        private static string ResolveRoamerPath()
        {
            string candidates = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
                "Autodesk", "Navisworks Manage 2026", "Roamer.exe");
            return File.Exists(candidates) ? candidates : "Roamer.exe";
        }
    }
}
