using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils;
using System;
using System.Collections.Generic;
using System.Linq;

namespace NavisworksTransport.Core.AliasTree
{
    /// <summary>
    /// 稳定的复合节点标识 — 替代 InstanceGuid 用于持久化。
    ///
    /// 双字段设计：
    /// - <see cref="IndexPath"/>：COM API 树索引路径（如 "0,2,5,1"），
    ///   复用 <see cref="ModelItemAnalysisHelper.GetModelItemIndexPaths"/> 生成。
    ///   定位时优先使用，精度高、速度快。
    /// - <see cref="DisplayPath"/>：父链 DisplayName 拼接路径（如 "模型/楼层/墙#0"），
    ///   可读可调试，IndexPath 失效时作为降级定位手段。
    ///
    /// 序列化格式（ToKey）： "IndexPath||DisplayPath"
    /// </summary>
    public readonly struct AliasNodeIdentity : IEquatable<AliasNodeIdentity>
    {
        /// <summary>COM 树索引路径（如 "0,2,5,1"），优先用于定位</summary>
        public string IndexPath { get; }

        /// <summary>父链 DisplayName 拼接路径 + 同级索引（如 "模型/楼层/墙#0"）</summary>
        public string DisplayPath { get; }

        /// <summary>
        /// 从全量信息构造
        /// </summary>
        public AliasNodeIdentity(string indexPath, string displayPath)
        {
            IndexPath = indexPath ?? string.Empty;
            DisplayPath = displayPath ?? throw new ArgumentNullException(nameof(displayPath));
        }

        // ── 工厂方法 ──

        /// <summary>
        /// 从 ModelItem 构造复合标识。
        /// 必须在主线程调用（涉及 Navisworks API + COM API）。
        /// </summary>
        public static AliasNodeIdentity FromModelItem(ModelItem item)
        {
            if (item == null)
                throw new ArgumentNullException(nameof(item));

            // COM API 必须在 STA 主线程
            if (System.Threading.Thread.CurrentThread.GetApartmentState() != System.Threading.ApartmentState.STA)
                LogManager.Error($"[别名树] FromModelItem 不在 STA 线程!");

            // 1. IndexPath — 使用 Navisworks 官方 API CreatePathId
            string indexPath;
            try
            {
                var pid = Autodesk.Navisworks.Api.Application.ActiveDocument.Models.CreatePathId(item);
                indexPath = $"{pid.ModelIndex}:{pid.PathId}";
            }
            catch (Exception ex)
            {
                LogManager.Error($"[别名树] CreatePathId 异常: {ex.Message}");
                indexPath = string.Empty;
            }

            // 2. DisplayPath — 父链 DisplayName + 每层同名去重索引
            var parts = new List<string>();
            var current = item;
            while (current != null)
            {
                int siblingIdx = ComputeSiblingIndex(current);
                string name = current.DisplayName ?? "?";
                parts.Insert(0, siblingIdx > 0 ? $"{name}#{siblingIdx}" : name);
                current = current.Parent;
            }
            string displayPathWithIndex = string.Join("/", parts);

            return new AliasNodeIdentity(indexPath, displayPathWithIndex);
        }

        /// <summary>
        /// 计算同名兄弟节点中的位置
        /// </summary>
        internal static int ComputeSiblingIndex(ModelItem item)
        {
            var parent = item.Parent;
            if (parent == null) return 0;

            string targetName = item.DisplayName;
            int matchIndex = 0;

            foreach (var child in parent.Children)
            {
                if (child.Equals(item))
                    return matchIndex;
                if (string.Equals(child.DisplayName, targetName, StringComparison.Ordinal))
                    matchIndex++;
            }

            return 0;
        }

        // ── 序列化 ──

        /// <summary>
        /// 序列化为 DB 主键 / JSON key
        /// 格式： "IndexPath||DisplayPath"
        /// </summary>
        public string ToKey()
        {
            if (string.IsNullOrEmpty(IndexPath))
                return DisplayPath;
            return $"{IndexPath}||{DisplayPath}";
        }

        /// <summary>
        /// 从 key 反序列化
        /// </summary>
        public static AliasNodeIdentity FromKey(string key)
        {
            if (string.IsNullOrEmpty(key))
                throw new ArgumentException("key 不能为空", nameof(key));

            // key 必须包含 "||" 分隔符
            int separatorIndex = key.IndexOf("||", StringComparison.Ordinal);
            if (separatorIndex < 0)
                throw new FormatException($"key 缺少 || 分隔符: {key}");

            string indexPath = key.Substring(0, separatorIndex);
            string displayPath = key.Substring(separatorIndex + 2);
            if (string.IsNullOrEmpty(displayPath))
                throw new FormatException($"DisplayPath 为空: {key}");
            return new AliasNodeIdentity(indexPath, displayPath);
        }

        /// <summary>
        /// 提前验证 key 字符串格式是否合法
        /// </summary>
        public static bool TryParseKey(string key, out AliasNodeIdentity identity)
        {
            identity = default;
            if (string.IsNullOrEmpty(key)) return false;

            try
            {
                identity = FromKey(key);
                return true;
            }
            catch
            {
                return false;
            }
        }

        // ── 查询辅助 ──

        /// <summary>
        /// 获取 DisplayPath 部分中的 HierarchicalPath（去掉尾部 #索引）
        /// </summary>
        public string GetHierarchicalPath()
        {
            int hashIdx = DisplayPath.LastIndexOf('#');
            return hashIdx > 0 ? DisplayPath.Substring(0, hashIdx) : DisplayPath;
        }

        /// <summary>
        /// 获取 DisplayPath 部分中的 SiblingIndex
        /// </summary>
        public int GetSiblingIndex()
        {
            int hashIdx = DisplayPath.LastIndexOf('#');
            if (hashIdx < 0 || hashIdx >= DisplayPath.Length - 1) return 0;
            string indexStr = DisplayPath.Substring(hashIdx + 1);
            int.TryParse(indexStr, out int idx);
            return idx;
        }

        /// <summary>
        /// 检查 DisplayPath 是否以指定前缀开头（用于"清除某分支下全部别名"）
        /// </summary>
        public bool HasPathPrefix(string prefix)
        {
            return GetHierarchicalPath().StartsWith(prefix, StringComparison.Ordinal);
        }

        // ── 值语义 ──

        public bool Equals(AliasNodeIdentity other)
        {
            return string.Equals(IndexPath, other.IndexPath, StringComparison.Ordinal)
                && string.Equals(DisplayPath, other.DisplayPath, StringComparison.Ordinal);
        }

        public override bool Equals(object obj)
            => obj is AliasNodeIdentity other && Equals(other);

        public override int GetHashCode()
        {
            unchecked
            {
                return (IndexPath?.GetHashCode() ?? 0 * 397) ^ (DisplayPath?.GetHashCode() ?? 0);
            }
        }

        public static bool operator ==(AliasNodeIdentity left, AliasNodeIdentity right)
            => left.Equals(right);

        public static bool operator !=(AliasNodeIdentity left, AliasNodeIdentity right)
            => !left.Equals(right);

        public override string ToString() => ToKey();
    }
}
