using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils;
using NavisworksTransport.Utils.CoordinateSystem;

namespace NavisworksTransport.Core
{
    /// <summary>
    /// 直线装配参考路径管理器。
    /// 使用单位立方体资源生成一根可点击的临时参考杆，供用户在 3D 视图中选择起点。
    /// 参考杆本质上表达的是一段 start->end 参考轴线，而不是终点处的独立构件。
    /// </summary>
    public class AssemblyReferencePathManager
    {
        private const double ReferenceRodBaseSizeInMeters = 0.01;
        private static readonly ModelAxisConvention ReferenceRodAxisConvention = ModelAxisConvention.CreateReferenceRodAssetConvention();

        private static AssemblyReferencePathManager _instance;
        private static readonly object _lock = new object();

        private Model _referenceRodModel;
        private ModelItem _referenceRodModelItem;
        private bool _isUpdatingReferenceRod;
        private bool _isReferenceRodVisible;
        private Point3D _referenceLineStart = Point3D.Origin;
        private Point3D _referenceLineEnd = Point3D.Origin;
        private double _referenceRodDiameterInMeters;
        private string _referenceResourceName;
        public static AssemblyReferencePathManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_lock)
                    {
                        if (_instance == null)
                        {
                            _instance = new AssemblyReferencePathManager();
                        }
                    }
                }

                return _instance;
            }
        }

        public ModelItem CurrentReferenceRod => _referenceRodModelItem;
        public bool IsReferenceRodVisible => _isReferenceRodVisible;
        public bool IsUpdatingReferenceRod => _isUpdatingReferenceRod;
        public bool HasReferenceLine => VectorLengthSquared(new Vector3D(
            _referenceLineEnd.X - _referenceLineStart.X,
            _referenceLineEnd.Y - _referenceLineStart.Y,
            _referenceLineEnd.Z - _referenceLineStart.Z)) > 1e-9;
        public Point3D ReferenceLineStart => _referenceLineStart;
        public Point3D ReferenceLineEnd => _referenceLineEnd;
        public string ReferenceResourceName => _referenceResourceName;

        private AssemblyReferencePathManager()
        {
            _isReferenceRodVisible = false;
        }

        /// <summary>
        /// 创建或更新参考杆。
        /// 先记录参考线主数据，再基于参考线更新可点击实体外壳。
        /// </summary>
        public ModelItem CreateOrUpdateReferenceRod(Point3D startPoint, Point3D endPoint, double diameterInMeters)
        {
            var doc = Application.ActiveDocument;
            if (doc == null || doc.IsClear)
            {
                throw new InvalidOperationException("当前没有可用文档，无法创建装配参考杆");
            }

            double referenceLength = GeometryHelper.CalculatePointDistance(startPoint, endPoint);
            if (referenceLength <= 1e-6)
            {
                throw new InvalidOperationException("参考线长度为 0，无法创建装配参考杆");
            }

            _isUpdatingReferenceRod = true;
            try
            {
                _referenceLineStart = startPoint;
                _referenceLineEnd = endPoint;
                _referenceRodDiameterInMeters = diameterInMeters;

                EnsureReferenceRodModelLoaded();

                ScaleReferenceRod(referenceLength, diameterInMeters);
                PositionReferenceRod();

                ShowReferenceRod();

                double referenceLengthInMeters = UnitsConverter.ConvertToMeters(referenceLength);
                LogManager.Info($"[装配参考杆] 已创建/更新，长度={referenceLengthInMeters:F3}m，直径={diameterInMeters:F3}m");
                return _referenceRodModelItem;
            }
            finally
            {
                _isUpdatingReferenceRod = false;
            }
        }

        public void ShowReferenceRod()
        {
            if (_referenceRodModelItem == null)
            {
                return;
            }

            var items = new ModelItemCollection { _referenceRodModel.RootItem };
            Application.ActiveDocument.Models.SetHidden(items, false);
            _isReferenceRodVisible = true;
        }

        public void HideReferenceRod()
        {
            if (_referenceRodModelItem == null)
            {
                return;
            }

            var items = new ModelItemCollection { _referenceRodModel.RootItem };
            Application.ActiveDocument.Models.SetHidden(items, true);
            _isReferenceRodVisible = false;
        }

        /// <summary>
        /// 将任意点击点投影到当前参考线。
        /// </summary>
        public Point3D ProjectPointToReferenceLine(Point3D point)
        {
            if (!HasReferenceLine)
            {
                throw new InvalidOperationException("当前没有可用的装配参考线");
            }

            return ProjectPointToSegment(point, _referenceLineStart, _referenceLineEnd);
        }

        public void Cleanup()
        {
            try
            {
                if (_referenceRodModel != null)
                {
                    var doc = Application.ActiveDocument;
                    if (doc != null && !doc.IsClear)
                    {
                        int index = doc.Models.IndexOf(_referenceRodModel);
                        if (index >= 0)
                        {
                            doc.TryRemoveFile(index);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogManager.Error($"清理装配参考杆失败: {ex.Message}");
            }
            finally
            {
                _referenceRodModel = null;
                _referenceRodModelItem = null;
                _isReferenceRodVisible = false;
                _referenceLineStart = Point3D.Origin;
                _referenceLineEnd = Point3D.Origin;
                _referenceRodDiameterInMeters = 0.0;
                _referenceResourceName = null;
            }
        }

        private void EnsureReferenceRodModelLoaded()
        {
            if (_referenceRodModel != null &&
                Application.ActiveDocument.Models.IndexOf(_referenceRodModel) >= 0 &&
                _referenceRodModelItem != null)
            {
                return;
            }

            string resourcePath = GetReferenceRodResourcePath(out string resourceName);
            if (string.IsNullOrEmpty(resourcePath) || !File.Exists(resourcePath))
            {
                throw new FileNotFoundException($"找不到参考杆资源文件: {resourcePath}");
            }

            var doc = Application.ActiveDocument;
            int modelCountBefore = doc.Models.Count;
            if (!doc.TryAppendFile(resourcePath))
            {
                throw new InvalidOperationException($"无法追加文件: {resourcePath}");
            }

            if (doc.Models.Count <= modelCountBefore)
            {
                throw new InvalidOperationException("追加参考杆资源后模型数量未增加");
            }

            _referenceRodModel = doc.Models.Last();
            _referenceRodModelItem = _referenceRodModel.RootItem;
            _isReferenceRodVisible = true;
            _referenceResourceName = resourceName;
            LogManager.Info($"[装配参考杆] 已加载资源: {resourceName}");
        }

        private void ScaleReferenceRod(double length, double diameterInMeters)
        {
            if (_referenceRodModel == null)
            {
                return;
            }

            var doc = Application.ActiveDocument;
            double lengthInMeters = UnitsConverter.ConvertToMeters(length);

            var currentTransform = _referenceRodModel.Transform;
            var components = currentTransform.Factor();
            components.Scale = ReferenceRodAxisConvention.CreateScaleVector(
                lengthInMeters / ReferenceRodBaseSizeInMeters,
                diameterInMeters / ReferenceRodBaseSizeInMeters,
                diameterInMeters / ReferenceRodBaseSizeInMeters);

            Transform3D scaledTransform = components.Combine();
            doc.Models.SetModelUnitsAndTransform(
                _referenceRodModel,
                _referenceRodModel.Units,
                scaledTransform,
                false);

            LogManager.Info(
                $"[装配参考杆] 缩放完成，目标长度={lengthInMeters:F3}m，目标直径={diameterInMeters:F3}m，模型单位={_referenceRodModel.Units}");
        }

        private void PositionReferenceRod()
        {
            if (_referenceRodModelItem == null || _referenceRodModel == null)
            {
                return;
            }

            Point3D targetCenter = new Point3D(
                (_referenceLineStart.X + _referenceLineEnd.X) / 2.0,
                (_referenceLineStart.Y + _referenceLineEnd.Y) / 2.0,
                (_referenceLineStart.Z + _referenceLineEnd.Z) / 2.0);

            Rotation3D rotation = CreateLineRotation(_referenceLineStart, _referenceLineEnd);
            ModelItemTransformHelper.MoveItemToCenterAndRotation(
                _referenceRodModelItem,
                targetCenter,
                rotation);

            LogManager.Info(
                $"[装配参考杆] 定位完成，中心=({targetCenter.X:F3}, {targetCenter.Y:F3}, {targetCenter.Z:F3})，起点=({_referenceLineStart.X:F3}, {_referenceLineStart.Y:F3}, {_referenceLineStart.Z:F3})，终点=({_referenceLineEnd.X:F3}, {_referenceLineEnd.Y:F3}, {_referenceLineEnd.Z:F3})");
        }

        private static Rotation3D CreateLineRotation(Point3D startPoint, Point3D endPoint)
        {
            var tangent = Normalize(new Vector3D(
                endPoint.X - startPoint.X,
                endPoint.Y - startPoint.Y,
                endPoint.Z - startPoint.Z));

            // 参考杆资源约定：几何中心在原点，长度轴沿本地 ForwardAxis。
            // Navisworks API 文档中 Rotation3D(vector1, vector2) 的语义
            // 是“将 vector1 旋转到与 vector2 同方向”，这里直接将资源本地 forward 轴对齐参考线方向。
            return new Rotation3D(
                new UnitVector3D(ReferenceRodAxisConvention.ForwardVector),
                new UnitVector3D(tangent));
        }

        private static Point3D ProjectPointToSegment(Point3D point, Point3D segmentStart, Point3D segmentEnd)
        {
            double segmentX = segmentEnd.X - segmentStart.X;
            double segmentY = segmentEnd.Y - segmentStart.Y;
            double segmentZ = segmentEnd.Z - segmentStart.Z;
            double lengthSquared = segmentX * segmentX + segmentY * segmentY + segmentZ * segmentZ;

            if (lengthSquared < 1e-9)
            {
                return segmentStart;
            }

            double pointX = point.X - segmentStart.X;
            double pointY = point.Y - segmentStart.Y;
            double pointZ = point.Z - segmentStart.Z;
            double projection = (pointX * segmentX + pointY * segmentY + pointZ * segmentZ) / lengthSquared;
            projection = Math.Max(0.0, Math.Min(1.0, projection));

            return new Point3D(
                segmentStart.X + projection * segmentX,
                segmentStart.Y + projection * segmentY,
                segmentStart.Z + projection * segmentZ);
        }

        private static Vector3D Normalize(Vector3D vector)
        {
            double lengthSquared = VectorLengthSquared(vector);
            if (lengthSquared < 1e-9)
            {
                return new Vector3D(1, 0, 0);
            }

            double length = Math.Sqrt(lengthSquared);
            return new Vector3D(vector.X / length, vector.Y / length, vector.Z / length);
        }

        private static double VectorLengthSquared(Vector3D vector)
        {
            return vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z;
        }

        private static string GetReferenceRodResourcePath(out string resourceName)
        {
            string pluginDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string[] preferredPaths = new[]
            {
                Path.Combine(pluginDir, "resources", "unit_cylinder.nwc"),
                Path.Combine(pluginDir, "..", "..", "resources", "unit_cylinder.nwc"),
                @"c:\Users\Tellme\apps\NavisworksTransport\resources\unit_cylinder.nwc",
                Path.Combine(pluginDir, "resources", "unit_cube.nwc"),
                Path.Combine(pluginDir, "..", "..", "resources", "unit_cube.nwc"),
                @"c:\Users\Tellme\apps\NavisworksTransport\resources\unit_cube.nwc"
            };

            foreach (string path in preferredPaths)
            {
                if (File.Exists(path))
                {
                    resourceName = Path.GetFileName(path);
                    return path;
                }
            }

            resourceName = "unit_cylinder.nwc";
            return preferredPaths[0];
        }
    }
}
