using System;
using NBF;
using UnityEngine;

[RequireComponent(typeof(LineRenderer))]
public class Rope : MonoBehaviour
{
    [Header("Anchors")] [SerializeField] public Rigidbody startAnchor;
    [SerializeField] public Rigidbody endAnchor;

    /// <summary>鱼线宽度倍数</summary>
    public int LineMultiple = 1;

    [Header("Physics (Dynamic Nodes, Fixed Segment Len)")] [SerializeField, Min(0.01f), Tooltip("物理每段固定长度（越小越细致越耗）")]
    private float physicsSegmentLen = 0.15f;

    [SerializeField, Range(2, 200)] private int minPhysicsNodes = 12;

    [SerializeField, Range(2, 400), Tooltip("物理节点上限（仅用于性能保护；与“最大长度不限制”不是一回事）")]
    private int maxPhysicsNodes = 120;

    [SerializeField] private float gravityStrength = 2.0f;
    [SerializeField, Range(0f, 1f)] private float velocityDampen = 0.95f;

    [SerializeField, Range(0.0f, 1.0f), Tooltip("约束修正强度，越大越硬。0.6~0.9 常用")]
    private float stiffness = 0.8f;

    [SerializeField, Range(1, 80), Tooltip("迭代次数。鱼线 10~30 通常够用")]
    private int iterations = 20;

    [Header("Length Control (No Min/Max Clamp)")]
    [Tooltip("初始总长度（米）。如果为 0，则用 physicsSegmentLen*(minPhysicsNodes-1) 作为初始长度")]
    [SerializeField, Min(0f)]
    private float initialLength = 0f;

    [Tooltip("长度变化平滑时间（越小越跟手，越大越稳）")] [SerializeField, Min(0.0001f)]
    private float lengthSmoothTime = 0.15f;

    [Tooltip("当长度在变化时，额外把速度压掉一些（防抖）。0=不额外处理，1=变化时几乎清速度（建议只在收线生效）")] [SerializeField, Range(0f, 1f)]
    private float lengthChangeVelocityKill = 0.6f;

    [Tooltip("允许的最小松弛余量（避免目标长度刚好等于锚点距离时抖动）")] [SerializeField, Min(0f)]
    private float minSlack = 0.002f;

    [Header("Head Segment Clamp")] [Tooltip("第一段（起点->第1节点）允许的最小长度，避免收线时第一段被压到0导致数值炸")] [SerializeField, Min(0.0001f)]
    private float headMinLen = 0.01f;

    [Header("Node Count Stability")] [SerializeField, Tooltip("节点数切换迟滞（米）。避免长度在临界点抖动导致节点数来回跳 -> 卡顿")]
    private float nodeHysteresis = 0.05f;

    [Header("Simple Ground/Water Constraint (Cheap)")] [SerializeField]
    private bool constrainToGround = true;

    [SerializeField] private LayerMask groundMask = ~0;
    [SerializeField, Min(0f)] private float groundRadius = 0.01f;
    [SerializeField, Min(0f)] private float groundCastHeight = 1.0f;
    [SerializeField, Min(0.01f)] private float groundCastDistance = 2.5f;
    
    [SerializeField, Range(1, 8), Tooltip("每隔多少个节点做一次地面检测；越大越省")]
    private int groundSampleStep = 3;

    [SerializeField, Tooltip("未采样的点用插值还是直接拷贝邻近采样值")]
    private bool groundInterpolate = true;
    
    [SerializeField, Range(1, 8), Tooltip("每隔多少次FixedUpdate更新一次地面约束")]
    private int groundUpdateEvery = 2;
    private int _groundFrameCounter;
    

    [Header("Render (High Resolution)")] [SerializeField, Min(1), Tooltip("静止时每段物理线段插值加密数量（越大越顺，越耗）")]
    private int renderSubdivisionsIdle = 6;

    [SerializeField, Min(1), Tooltip("甩动时每段物理线段插值加密数量（动态降LOD以防卡顿）")]
    private int renderSubdivisionsMoving = 2;

    [SerializeField, Min(0f), Tooltip("平均速度超过该阈值认为在甩动（用于动态降 subdiv）")]
    private float movingSpeedThreshold = 2.0f;

    [SerializeField, Tooltip("是否使用 Catmull-Rom 平滑（开启更顺，但更耗）")]
    private bool smooth = true;

    [SerializeField, Min(0.0001f)] private float lineWidth = 0.001f;

    [Header("Air Drag (Stable)")] [SerializeField, Range(0f, 5f), Tooltip("空气阻力（Y向），指数衰减，越大越不飘")]
    private float airDrag = 0.9f;

    [SerializeField, Range(0f, 2f), Tooltip("横向额外阻力（XZ），指数衰减，越大越不左右飘")]
    private float airDragXZ = 0.6f;

    private LineRenderer _lineRenderer;

    // physics
    private int _physicsNodes;
    private Vector3[] _pCurr;
    private Vector3[] _pPrev;

    // render (一次性分配到最大，后续不再 new)
    private Vector3[] _rPoints;
    private int _rCapacity;

    private Vector3 _gravity;

    // length control runtime
    private float _targetLength;
    private float _currentLength;
    private float _lengthSmoothVel;

    // rest length head
    private float _headRestLen;

    // node stability
    private int _lastDesiredNodes = 0;

    // caches
    private Transform _startTr;
    private Transform _endTr;

    // precomputed
    private float _dt;
    private float _dt2;
    private float _kY;
    private float _kXZ;

    private FRod _rod;
    public void Init(FRod rod) => _rod = rod;

    // Catmull t caches（只缓存 idle/moving 两档，减少每帧重复乘法）
    private struct TCaches
    {
        public float[] t;
        public float[] t2;
        public float[] t3;
    }

    private TCaches _tIdle;
    private TCaches _tMoving;

    private void Awake()
    {
        _lineRenderer = GetComponent<LineRenderer>();
        _gravity = new Vector3(0f, -gravityStrength, 0f);

        _startTr = startAnchor ? startAnchor.transform : null;
        _endTr = endAnchor ? endAnchor.transform : null;

        InitLengthSystem();
        AllocateAndInitNodes();

        // ✅ 渲染点一次性分配到最大： (maxNodes-1)*idle + 1
        int maxSubdiv = Mathf.Max(1, renderSubdivisionsIdle);
        _rCapacity = (maxPhysicsNodes - 1) * maxSubdiv + 1;
        _rPoints = new Vector3[_rCapacity];

        BuildTCaches(renderSubdivisionsIdle, ref _tIdle);
        BuildTCaches(renderSubdivisionsMoving, ref _tMoving);
    }

    private void OnValidate()
    {
        renderSubdivisionsIdle = Mathf.Max(renderSubdivisionsIdle, 1);
        renderSubdivisionsMoving = Mathf.Max(renderSubdivisionsMoving, 1);
        iterations = Mathf.Clamp(iterations, 1, 80);
        groundCastDistance = Mathf.Max(groundCastDistance, 0.01f);
        groundCastHeight = Mathf.Max(groundCastHeight, 0f);
        lineWidth = Mathf.Max(lineWidth, 0.0001f);

        lengthSmoothTime = Mathf.Max(lengthSmoothTime, 0.0001f);

        physicsSegmentLen = Mathf.Max(physicsSegmentLen, 0.01f);
        minPhysicsNodes = Mathf.Max(minPhysicsNodes, 2);
        maxPhysicsNodes = Mathf.Max(maxPhysicsNodes, minPhysicsNodes);

        headMinLen = Mathf.Max(headMinLen, 0.0001f);
        nodeHysteresis = Mathf.Max(0f, nodeHysteresis);
    }

    private void InitLengthSystem()
    {
        float defaultLen = physicsSegmentLen * (Mathf.Max(minPhysicsNodes, 2) - 1);
        _currentLength = (initialLength > 0f) ? initialLength : defaultLen;
        _targetLength = _currentLength;
    }

    private void AllocateAndInitNodes()
    {
        _physicsNodes = Mathf.Clamp(ComputeDesiredNodesStable(_currentLength), 2, maxPhysicsNodes);

        _pCurr = new Vector3[maxPhysicsNodes];
        _pPrev = new Vector3[maxPhysicsNodes];

        Vector3 start = startAnchor ? startAnchor.position : transform.position;
        Vector3 dir = Vector3.down;

        for (int i = 0; i < _physicsNodes; i++)
        {
            Vector3 pos = start + dir * (physicsSegmentLen * i);
            _pCurr[i] = pos;
            _pPrev[i] = pos;
        }

        UpdateHeadRestLenFromCurrentLength();

        if (startAnchor && endAnchor)
            LockAnchorsHard();
    }

    private int ComputeDesiredNodes(float lengthMeters)
    {
        int desired = Mathf.FloorToInt(Mathf.Max(0f, lengthMeters) / physicsSegmentLen) + 1;
        desired = Mathf.Clamp(desired, minPhysicsNodes, maxPhysicsNodes);
        return desired;
    }

    private int ComputeDesiredNodesStable(float lengthMeters)
    {
        int desired = ComputeDesiredNodes(lengthMeters);

        if (_lastDesiredNodes == 0)
        {
            _lastDesiredNodes = desired;
            return desired;
        }

        if (desired == _lastDesiredNodes)
            return desired;

        float boundary = (_lastDesiredNodes - 1) * physicsSegmentLen;
        if (Mathf.Abs(lengthMeters - boundary) < nodeHysteresis)
            return _lastDesiredNodes;

        _lastDesiredNodes = desired;
        return desired;
    }

    public void SetTargetLength(float lengthMeters) => _targetLength = Mathf.Max(0f, lengthMeters);
    public float GetCurrentLength() => _currentLength;
    public float GetTargetLength() => _targetLength;

    private void FixedUpdate()
    {
        if (!startAnchor || !endAnchor) return;

        // cache dt
        _dt = Time.fixedDeltaTime;
        if (_dt < 1e-6f) _dt = 1e-6f;
        _dt2 = _dt * _dt;

        // gravity
        _gravity.y = -gravityStrength;

        // drag caches（exp 比较贵，但这里每 FixedUpdate 一次，OK）
        _kY = Mathf.Exp(-airDrag * _dt);
        _kXZ = Mathf.Exp(-airDragXZ * _dt);

        UpdateLengthSmooth();
        UpdateNodesFromLength();
        UpdateHeadRestLenFromCurrentLength();

        Simulate_VerletFast();

        // anchors
        LockAnchorsHard();

        // constraints
        for (int it = 0; it < iterations; it++)
            SolveDistanceConstraints_HeadOnly_Fast();

        LockAnchorsHard();

        if (constrainToGround)
        {
            _groundFrameCounter++;
            if (_groundFrameCounter >= groundUpdateEvery)
            {
                _groundFrameCounter = 0;
                ConstrainToGround();
            }
        }

        LockAnchorsHard();
    }

    private void LateUpdate()
    {
        if (!startAnchor || !endAnchor || _pCurr == null || _physicsNodes < 2) return;

        int last = _physicsNodes - 1;

        // 用缓存 transform，避免多次属性链
        Vector3 s = _startTr.position;
        Vector3 e = _endTr.position;

        _pCurr[0] = s;
        _pPrev[0] = s;
        _pCurr[last] = e;
        _pPrev[last] = e;

        DrawHighResLine_Fast();
    }

    private void UpdateLengthSmooth()
    {
        float minFeasible = 0.01f;
        float desired = Mathf.Max(_targetLength, minFeasible);

        _currentLength = Mathf.SmoothDamp(
            _currentLength,
            desired,
            ref _lengthSmoothVel,
            lengthSmoothTime,
            Mathf.Infinity,
            Time.fixedDeltaTime
        );
    }

    private void UpdateNodesFromLength()
    {
        int desired = ComputeDesiredNodesStable(_currentLength);
        desired = Mathf.Clamp(desired, 2, maxPhysicsNodes);
        if (desired == _physicsNodes) return;

        if (desired > _physicsNodes) AddNodesAtStart(desired - _physicsNodes);
        else RemoveNodesAtStart(_physicsNodes - desired);

        _physicsNodes = desired;
    }

    private void AddNodesAtStart(int addCount)
    {
        if (addCount <= 0) return;

        int oldCount = _physicsNodes;
        int newCount = Mathf.Min(oldCount + addCount, maxPhysicsNodes);
        addCount = newCount - oldCount;
        if (addCount <= 0) return;

        Array.Copy(_pCurr, 1, _pCurr, 1 + addCount, oldCount - 1);
        Array.Copy(_pPrev, 1, _pPrev, 1 + addCount, oldCount - 1);

        Vector3 s = _startTr ? _startTr.position : startAnchor.position;

        Vector3 dir = Vector3.down;
        int firstOld = 1 + addCount;
        if (oldCount >= 2 && firstOld < maxPhysicsNodes)
        {
            Vector3 toOld1 = (_pCurr[firstOld] - s);
            float sq = toOld1.sqrMagnitude;
            if (sq > 1e-6f) dir = toOld1 / Mathf.Sqrt(sq);
        }

        // inherit displacement (Verlet)
        Vector3 inheritDisp = Vector3.zero;
        if (oldCount >= 2 && firstOld < maxPhysicsNodes)
            inheritDisp = (_pCurr[firstOld] - _pPrev[firstOld]);

        for (int k = 1; k <= addCount; k++)
        {
            Vector3 pos = s + dir * (physicsSegmentLen * k);
            _pCurr[k] = pos;
            _pPrev[k] = pos - inheritDisp; // 保持动感
        }

        LockAnchorsHard();
    }

    private void RemoveNodesAtStart(int removeCount)
    {
        if (removeCount <= 0) return;

        int oldCount = _physicsNodes;
        int newCount = Mathf.Max(oldCount - removeCount, 2);
        removeCount = oldCount - newCount;
        if (removeCount <= 0) return;

        Array.Copy(_pCurr, 1 + removeCount, _pCurr, 1, newCount - 2);
        Array.Copy(_pPrev, 1 + removeCount, _pPrev, 1, newCount - 2);

        LockAnchorsHard();
    }

    private void UpdateHeadRestLenFromCurrentLength()
    {
        int fixedSegCount = Mathf.Max(0, _physicsNodes - 2);
        float baseLen = fixedSegCount * physicsSegmentLen;

        _headRestLen = _currentLength - baseLen;
        _headRestLen = Mathf.Clamp(_headRestLen, headMinLen, physicsSegmentLen * 1.5f);
    }

    /// <summary>
    /// ✅ 更快的 Verlet：去掉 /dt 和 *dt 抵消的无效计算
    /// </summary>
    private void Simulate_VerletFast()
    {
        // displacement = curr - prev
        // next = curr + displacement*drag*dampen + gravity*dt^2
        for (int i = 1; i < _physicsNodes - 1; i++)
        {
            Vector3 disp = _pCurr[i] - _pPrev[i];

            disp.x *= _kXZ;
            disp.z *= _kXZ;
            disp.y *= _kY;

            disp *= velocityDampen;

            Vector3 next = _pCurr[i] + disp + _gravity * _dt2;

            _pPrev[i] = _pCurr[i];
            _pCurr[i] = next;
        }
    }

    private void LockAnchorsHard()
    {
        if (!startAnchor || !endAnchor || _pCurr == null || _pPrev == null || _physicsNodes < 2) return;

        Vector3 s = _startTr ? _startTr.position : startAnchor.position;
        Vector3 e = _endTr ? _endTr.position : endAnchor.position;

        _pCurr[0] = s;
        _pPrev[0] = s - startAnchor.linearVelocity * _dt;

        int last = _physicsNodes - 1;
        _pCurr[last] = e;
        _pPrev[last] = e - endAnchor.linearVelocity * _dt;
    }

    /// <summary>
    /// ✅ 约束：减少临时变量、用 sqrMagnitude + invDist
    /// </summary>
    private void SolveDistanceConstraints_HeadOnly_Fast()
    {
        int last = _physicsNodes - 1;

        for (int i = 0; i < last; i++)
        {
            float rest = (i == 0) ? _headRestLen : physicsSegmentLen;

            Vector3 a = _pCurr[i];
            Vector3 b = _pCurr[i + 1];

            Vector3 delta = b - a;
            float sq = delta.sqrMagnitude;
            if (sq < 1e-12f) continue;

            float dist = Mathf.Sqrt(sq);
            float diff = (dist - rest) / dist; // = 1 - rest/dist
            Vector3 corr = delta * (diff * stiffness);

            // i==0 锚点固定；last 锚点固定
            if (i != 0) _pCurr[i] = a + corr * 0.5f;
            if (i + 1 != last) _pCurr[i + 1] = b - corr * 0.5f;
        }
    }

    // private void ConstrainToGround()
    // {
    //     if (groundMask == 0) return;
    //
    //     // RaycastHit 是 struct，这里不会 GC
    //     for (int i = 1; i < _physicsNodes - 1; i++)
    //     {
    //         Vector3 p = _pCurr[i];
    //         Vector3 origin = p + Vector3.up * groundCastHeight;
    //
    //         if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, groundCastDistance, groundMask,
    //                 QueryTriggerInteraction.Ignore))
    //         {
    //             float minY = hit.point.y + groundRadius;
    //             if (p.y < minY) p.y = minY;
    //         }
    //
    //         _pCurr[i] = p;
    //     }
    // }

    private void ConstrainToGround()
    {
        if (groundMask == 0) return;

        int last = _physicsNodes - 1;
        int step = Mathf.Max(1, groundSampleStep);

        // 记录采样点的“最低允许Y”
        // 不想分配数组就用局部变量滚动插值
        int prevSampleIdx = 1;
        float prevMinY = SampleMinY(_pCurr[prevSampleIdx]);

        // 把采样点先处理掉
        ApplyMinY(prevSampleIdx, prevMinY);

        for (int i = 1 + step; i < last; i += step)
        {
            float nextMinY = SampleMinY(_pCurr[i]);
            ApplyMinY(i, nextMinY);

            if (groundInterpolate)
            {
                // 在两个采样点之间插值 minY（视觉更平滑）
                int a = prevSampleIdx;
                int b = i;
                int span = b - a;
                for (int j = 1; j < span; j++)
                {
                    int idx = a + j;
                    float t = j / (float)span;
                    float minY = Mathf.Lerp(prevMinY, nextMinY, t);
                    ApplyMinY(idx, minY);
                }
            }
            else
            {
                // 直接用 prevMinY 填充中间点（更省）
                for (int idx = prevSampleIdx + 1; idx < i; idx++)
                    ApplyMinY(idx, prevMinY);
            }

            prevSampleIdx = i;
            prevMinY = nextMinY;
        }

        // 尾巴剩余部分用最后一个采样值填
        for (int i = prevSampleIdx + 1; i < last; i++)
            ApplyMinY(i, prevMinY);
    }

    private float SampleMinY(Vector3 p)
    {
        Vector3 origin = p + Vector3.up * groundCastHeight;
        if (Physics.Raycast(origin, Vector3.down, out RaycastHit hit, groundCastDistance, groundMask,
                QueryTriggerInteraction.Ignore))
            return hit.point.y + groundRadius;

        // 没命中就不抬（返回极小值）
        return float.NegativeInfinity;
    }

    private void ApplyMinY(int i, float minY)
    {
        if (float.IsNegativeInfinity(minY)) return;
        Vector3 p = _pCurr[i];
        if (p.y < minY) p.y = minY;
        _pCurr[i] = p;
    }

    private void DrawHighResLine_Fast()
    {
        if (_pCurr == null || _physicsNodes < 2) return;

        float w = lineWidth * LineMultiple;
        _lineRenderer.startWidth = w;
        _lineRenderer.endWidth = w;

        if (!smooth)
        {
            _lineRenderer.positionCount = _physicsNodes;
            _lineRenderer.SetPositions(_pCurr);
            return;
        }

        int subdiv = PickRenderSubdivisions_Fast();
        TCaches tc = (subdiv == renderSubdivisionsMoving) ? _tMoving : _tIdle;

        int needed = (_physicsNodes - 1) * subdiv + 1;
        if (needed > _rCapacity)
        {
            // 理论上不该发生（_rCapacity 用 maxNodes & idle 分配）
            // 保险扩容一次
            _rCapacity = needed;
            _rPoints = new Vector3[_rCapacity];
        }

        int idx = 0;
        int last = _physicsNodes - 1;

        for (int seg = 0; seg < last; seg++)
        {
            int i0 = seg - 1;
            if (i0 < 0) i0 = 0;
            int i1 = seg;
            int i2 = seg + 1;
            int i3 = seg + 2;
            if (i3 > last) i3 = last;

            Vector3 p0 = _pCurr[i0];
            Vector3 p1 = _pCurr[i1];
            Vector3 p2 = _pCurr[i2];
            Vector3 p3 = _pCurr[i3];

            for (int s = 0; s < subdiv; s++)
            {
                float t = tc.t[s];
                float t2 = tc.t2[s];
                float t3 = tc.t3[s];

                // inline CatmullRom（少一次函数调用）
                Vector3 cr =
                    0.5f * (
                        (2f * p1) +
                        (-p0 + p2) * t +
                        (2f * p0 - 5f * p1 + 4f * p2 - p3) * t2 +
                        (-p0 + 3f * p1 - 3f * p2 + p3) * t3
                    );

                // Linear Y
                cr.y = p1.y + (p2.y - p1.y) * t;

                _rPoints[idx++] = cr;
            }
        }

        _rPoints[idx++] = _pCurr[last];

        _lineRenderer.positionCount = idx;
        _lineRenderer.SetPositions(_rPoints);
    }

    /// <summary>
    /// ✅ 用 sqrMagnitude 比较阈值，避免 sqrt
    /// </summary>
    private int PickRenderSubdivisions_Fast()
    {
        int idle = Mathf.Max(1, renderSubdivisionsIdle);
        int moving = Mathf.Max(1, renderSubdivisionsMoving);

        float thr = movingSpeedThreshold;
        float thrSq = (thr * _dt) * (thr * _dt); // 因为我们用 disp = curr-prev（单位是米/step），所以阈值要乘 dt

        float sumSq = 0f;
        int count = Mathf.Max(1, _physicsNodes - 2);

        for (int i = 1; i < _physicsNodes - 1; i++)
        {
            Vector3 disp = _pCurr[i] - _pPrev[i];
            sumSq += disp.sqrMagnitude;
        }

        float avgSq = sumSq / count;

        return (avgSq > thrSq) ? moving : idle;
    }

    private static void BuildTCaches(int subdiv, ref TCaches caches)
    {
        subdiv = Mathf.Max(1, subdiv);
        caches.t = new float[subdiv];
        caches.t2 = new float[subdiv];
        caches.t3 = new float[subdiv];

        float inv = 1f / subdiv;
        for (int s = 0; s < subdiv; s++)
        {
            float t = s * inv;
            float t2 = t * t;
            caches.t[s] = t;
            caches.t2[s] = t2;
            caches.t3[s] = t2 * t;
        }
    }

    private void OnDrawGizmosSelected()
    {
        if (_pCurr == null) return;
        Gizmos.color = Color.yellow;
        for (int i = 0; i < _physicsNodes; i++)
            Gizmos.DrawSphere(_pCurr[i], 0.01f);
    }
}