using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DG.Tweening;
using FairyGUI;
using FGUI.Arrow_game;
using FGUI.Common_01;
using IgnoreOPS;
using UnityEngine;
using btn_saveingpot = FGUI.Common_01.btn_saveingpot;
using Random = UnityEngine.Random;

namespace ChillConnect
{
    public class ArrowGameUI : BaseUI
    {
        private ArrowGameUICtrl ctrl;
        private com_arrow_game ui;

        // FGUI界面根容器
        private GComponent _viewContainer;

        // 关卡总配置数据
        private LevelConfig _levelConfig;

        // 网格基础参数
        private int gridColNum = 10;  
        private int gridRowNum = 10;  
        private readonly float pointSpace = 60f;
        private readonly float outOffset = 200f;

        // 网格边界
        private Vector2 _gridWorldCenter; // 网格世界中心点
        
        private float gridLeft;
        private float gridRight;
        private float gridTop;
        private float gridBottom;

        // 网格行列、单个格子尺寸（你原有网格参数）
        private int _gridRow;
        private int _gridCol;
        private float _cellSize;
        
        // 网格占用标记：true=该点位被占用，false=空闲
        private bool[,] _gridOccupied;
        // 记录每个箭头占用的所有网格点位(行,列)
        private Dictionary<ArrowConfig, List<(int row, int col)>> _arrowOccupiedCells = new Dictionary<ArrowConfig, List<(int row, int col)>>();
        
        // 爱心数量
        private int _heartCount = 3;
        // 记录已经发生过碰撞的箭头对象（去重：同个箭头多次碰撞只扣一次血）
        private HashSet<ArrowConfig> _collidedArrows = new HashSet<ArrowConfig>();
        // 统计场上还存在的箭头总数（用于判断全部移除=通关）
        private int _remainArrowCount = 0;

        // 记录当前UI所有由 CrazyAsyKit 启动的协程，用于统一停止
        private List<Coroutine> _activeCoroutines = new List<Coroutine>();
        
        // 是否开启删除道具模式
        private bool _isDeleteMode = false;
        
        // 手指提示图标
        private com_finger _fingerTipObj;
        // 当前正在提示的箭头
        private ArrowConfig _currentTipArrow;
        // 手指图标偏移（微调位置）
        private readonly Vector2 _fingerOffset = new Vector2(-20, -20);
        // 所有网格圆点实例列表，用于批量改色/清理
        private List<ArrorPoint> _gridDotList = new List<ArrorPoint>();
        
        #region 箭头颜色配置
        // 8种预设彩色，可按需调整色值
        private readonly Color[] _colorfulPalette = new Color[]
        {
            new Color(1f, 0.3f, 0.3f),    // 红
            new Color(1f, 0.6f, 0.2f),    // 橙
            new Color(1f, 0.9f, 0.2f),    // 黄
            new Color(0.3f, 0.8f, 0.4f),  // 绿
            new Color(0.2f, 0.8f, 0.9f),  // 青
            new Color(0.3f, 0.5f, 1f),    // 蓝
            new Color(0.7f, 0.4f, 1f),    // 紫
            new Color(1f, 0.5f, 0.8f)     // 粉
        };
        #endregion
        
        #region 缩放、拖拽、滑动条 全局变量
        // 缩放阈值（滑动条左右边界对应这两个值）
        private readonly float _minScale = 0.5f;
        private readonly float _maxScale = 1.5f;
        // FGUI 滑动条组件
        private GSlider _zoomSlider;
        // 双指缩放手势
        private float _lastTwoFingerDistance;
        private bool _isTwoFingerTouch;
        // 单指拖拽平移
        private Vector2 _lastTouchPos;
        private bool _isDraging;
        // 拖拽边界留边（防止完全移出屏幕）
        private readonly float _dragBorder = -300f;
        #endregion
        
        
        public ArrowGameUI(ArrowGameUICtrl ctrl) : base(ctrl)
        {
            uiName = UIConst.ArrowGameUI;
            this.ctrl = ctrl;
        }

        protected override void SetUIInfo(UIInfo uiInfo)
        {
            uiInfo.packageName = "Arrow_game";
            uiInfo.assetName = "com_arrow_game";
            uiInfo.layerType = UILayerType.Bottom;
            uiInfo.isNeedOpenAnim = false;
            uiInfo.isNeedCloseAnim = false;
            uiInfo.isNeedUIMask = true;
            uiInfo.isTickUpdate = true;
        }

        #region 生命周期
        protected override void OnInit()
        {
        }

        protected override void OnClose()
        {
            // ========= 第一步：统一停止所有协程（核心修复） =========
            foreach (var co in _activeCoroutines)
            {
                if (co != null)
                {
                    CrazyAsyKit.StopCoroutine(co);
                }
            }
            // 清空列表
            _activeCoroutines.Clear();
            
            // 保险========= 强制停止所有移动，终止对应协程 =========
            if (_levelConfig != null)
            {
                foreach (var arrow in _levelConfig.arrows)
                {
                    arrow.isMoving = false;
                }
            }
            
            // 销毁所有动态FGUI对象
            ClearAllDynamicObj();
            
            HideFingerTip();

            _levelConfig = null;

            // 清空网格占用数据
            _gridOccupied = null;
            _arrowOccupiedCells.Clear();
            
        }

        protected override void OnBind()
        {
            ui = baseUI as com_arrow_game;
        }
        
        // 新增坐标转换方法
        private Vector2 UnityMouseToFGUIPos(Vector2 unityPos)
        {
            float x = unityPos.x;
            float y = Screen.height - unityPos.y;
            return new Vector2(x, y);
        }

        #region 全局计数式开关
        // 全局开关(true： 恢复输入  false：禁用输入)
        private void OpenOrStopGameInput(object obj = null)
        {
            if (obj != null && !(bool)obj)
            {
                // 禁用输入
                DisableGameInput();
            }
            else
            {
                // 恢复输入
                EnableGameInput();
            }
        }
        
        private int _inputDisableCount = 0;
        // 禁用输入
        private void DisableGameInput()
        {
            _inputDisableCount++;
            // 只有第一次禁用时执行清理
            if (_inputDisableCount == 1)
            {
                if (_viewContainer != null)
                    _viewContainer.touchable = false;
            
                _isDraging = false;
                _isTwoFingerTouch = false;
                _lastTouchPos = Vector2.zero;
            }
        }

        // 恢复输入
        private void EnableGameInput(object obj = null)
        {
            _inputDisableCount = Mathf.Max(0, _inputDisableCount - 1);
            // 计数归0时才真正恢复
            if (_inputDisableCount == 0)
            {
                if (_viewContainer != null)
                    _viewContainer.touchable = true;
            }
        }

        #endregion

        public override void OnUpdate()
        {
            // 禁用输入时，直接跳过拖拽
            if (_inputDisableCount > 0)
            {
                // Debug.Log($"输入已禁用，直接跳过拖拽");
                return;
            }
            
            // 初始化未完成，直接跳过拖拽
            if (!_initComplete)
                return;
            
#if UNITY_EDITOR
            float moveThreshold = 1f;

            // 鼠标按下：仅在拖拽区域内按下才开启拖拽
            if (InputHelper.Instance.GetMouseButtonDown(0))
            {
                Vector2 unityPos = InputHelper.Instance.MousePosition;
                Vector2 fguiPos = UnityMouseToFGUIPos(unityPos);

                if (IsInDragArea(fguiPos))
                {
                    _lastTouchPos = fguiPos;
                    _isDraging = true;
                    // Debug.Log("鼠标按下，在拖拽区域内，开始拖拽");
                }
                return;
            }

            // 拖拽中：原有逻辑不变
            if (_isDraging && InputHelper.Instance.GetMouseButton(0))
            {
                Vector2 unityPos = InputHelper.Instance.MousePosition;
                Vector2 curMousePos = UnityMouseToFGUIPos(unityPos);
                Vector2 offset = curMousePos - _lastTouchPos;

                if (offset.magnitude > moveThreshold)
                {
                    MoveContainer(offset);
                    _lastTouchPos = curMousePos;
                }
            }

            // 鼠标抬起，结束拖拽
            if (InputHelper.Instance.GetMouseButtonUp(0))
            {
                _isDraging = false;
                // Debug.Log("鼠标抬起，结束拖拽");
            }
#endif
            
            // ========= 双指 = 缩放（优先级最高） =========
            if (InputHelper.Instance.TouchCount == 2)
            {
                _isDraging = false;
                Touch t0 = InputHelper.Instance.GetTouch(0);
                Touch t1 = InputHelper.Instance.GetTouch(1);
                float currentDis = Vector2.Distance(t0.position, t1.position);

                if (!_isTwoFingerTouch)
                {
                    _lastTwoFingerDistance = currentDis;
                    _isTwoFingerTouch = true;
                }
                else
                {
                    float delta = currentDis - _lastTwoFingerDistance;
                    float curScale = _viewContainer.scaleX;
                    // 缩放灵敏度，按需微调
                    curScale += delta * 0.001f;
                    curScale = Mathf.Clamp(curScale, _minScale, _maxScale);

                    // 应用缩放
                    _viewContainer.scaleX = curScale;
                    _viewContainer.scaleY = curScale;

                    // 关键：双指缩放后，同步更新滑动条位置
                    _zoomSlider.value = GetSliderValueByScale(curScale);

                    _lastTwoFingerDistance = currentDis;
                }
            }
            // ========= 单指 = 拖拽平移 =========
            else if (InputHelper.Instance.TouchCount == 1)
            {
                Touch touch = InputHelper.Instance.GetTouch(0);
                // 触摸坐标和鼠标同属Unity屏幕坐标系，统一转成FGUI坐标系
                Vector2 curTouchPos = UnityMouseToFGUIPos(touch.position);

                if (touch.phase == TouchPhase.Began)
                {
                    // 只有在拖拽区域内按下，才开启拖拽
                    if (IsInDragArea(curTouchPos))
                    {
                        _lastTouchPos = curTouchPos;
                        _isDraging = true;
                        _isTwoFingerTouch = false;
                    }
                    
                    // 只有点击在网格范围内才触发震动
                    // if (IsInGridArea(curTouchPos))
                    // {
                    //     TriggerVibration();
                    // }
                }
                else if (touch.phase == TouchPhase.Moved && _isDraging)
                {
                    Vector2 offset = curTouchPos - _lastTouchPos;
                    MoveContainer(offset);
                    _lastTouchPos = curTouchPos;
                }
                else if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
                {
                    _isDraging = false;
                }
            }
            // 无触摸，重置状态
            else
            {
                _isTwoFingerTouch = false;
                _isDraging = false;
            }
        }

        protected override void OnOpenBefore(object args)
        {
            if (Screen.safeArea.y != 0)
            {//刘海屏
                ui.panel.y += Screen.safeArea.y - 25;
                ui.com_money.y += Screen.safeArea.y- 15;
                ui.com_gem.y += Screen.safeArea.y - 15;
                ui.btn_signin.y += Screen.safeArea.y - 25;
                ui.com_gift.y += Screen.safeArea.y - 15;
                ui.btn_wv.y += Screen.safeArea.y - 25;
            }

            ui.state.selectedIndex = GameHelper.IsGiftSwitch() ? 1 : 0;
            
            
            //加载配置
            // InitAllLevelData();
            LoadLevelConfig();
            _viewContainer = ui.view_container_parent.panel;
            InitGridBounds();

            // 初始化网格占用数组
            InitGridOccupied();
            
            // ========== 新增：对局状态重置 ==========
            _heartCount = 3;
            _collidedArrows.Clear();
            if(_levelConfig != null)
                _remainArrowCount = _levelConfig.arrows.Count;
            
            InitView();
            
            ((com_money)ui.com_money).btn_ch.title = GameHelper.getDesByKey("ch_out_1");
            
            if (GameHelper.IsGiftSwitch() && ConfigSystem.GetConfig<CommonModel>().PiggyBankSwitch == 1)
            {
                ui.btn_saveingpot.visible = true;
                ui.com_gift.visible =  true;
            }
            
            GameHelper.IsShowFirstReward();

            GameHelper.IsShowPettyReward();
            
            GameHelper.ShowStatementView();
        }

        private void RefreshGift(object obj = null)
        {
            ui.com_gift.text_gift.text = GameHelper.Get102Str((decimal)SaveData.GetSaveObject().saveingpot_ch);
        }    

        private void Set101(decimal coin = -1)
        {
            if (coin < 0)
            {
                coin = DataMgr.Coin.Value;
            }

            if (ui.com_gem is com_gold btnCoin) btnCoin.text_gold.text = $"{coin:N0}";
        }

        private void Set102()
        {
            decimal coin = DataMgr.Ticket.Value;

            if (ui.com_money is com_money comMoney) comMoney.text_gold.text = coin.ToString("0.00");
        }
        
        public void OnUpdate101(object obj = null)
        {
            Set101();
        }

        public void OnUpdate102(object obj = null)
        {
            Set102();
        }

        protected override void OnOpen(object args)
        {
        }

        protected override void OnHide()
        {
        }

        protected override void OnDisplay(object args)
        {
        }
        #endregion
        
        #region 消息监听
        protected override void AddListener()
        {
            GameDispatcher.Instance.AddListener(GameMsg.reset_game, OnRestartGame);
            GameDispatcher.Instance.AddListener(GameMsg.Update102, SetTopCurr);
            GameDispatcher.Instance.AddListener(GameMsg.Update101, SetTopCurr);
            GameDispatcher.Instance.AddListener(GameMsg.ThemeChange, SetModel);
            GameDispatcher.Instance.AddListener(GameMsg.UpdateSpeed, SetSpeed);
            GameDispatcher.Instance.AddListener(GameMsg.UseProps, SetUserPorp);
            GameDispatcher.Instance.AddListener(GameMsg.StopArrowTouch,OpenOrStopGameInput);
            GameDispatcher.Instance.AddListener(GameMsg.refreshGift,RefreshGift);
            
        }

        protected override void RemoveListener()
        {
            GameDispatcher.Instance.RemoveListener(GameMsg.reset_game, OnRestartGame);
            GameDispatcher.Instance.RemoveListener(GameMsg.Update102, SetTopCurr);
            GameDispatcher.Instance.RemoveListener(GameMsg.Update101, SetTopCurr);
            GameDispatcher.Instance.RemoveListener(GameMsg.ThemeChange, SetModel);
            GameDispatcher.Instance.RemoveListener(GameMsg.UpdateSpeed, SetSpeed);
            GameDispatcher.Instance.RemoveListener(GameMsg.UseProps, SetUserPorp);
            GameDispatcher.Instance.RemoveListener(GameMsg.StopArrowTouch, OpenOrStopGameInput);
            GameDispatcher.Instance.RemoveListener(GameMsg.refreshGift,RefreshGift);
            
            
            
            
        }
        #endregion

        #region 页面初始化 
        private bool _initComplete = false;
        private void InitView()
        {
            // 重置手势状态
            _isTwoFingerTouch = false;
            _isDraging = false;
            

            ui.text_level.SetVar("lv", GameHelper.GetLevel().ToString()).FlushVars();
            
            // 按钮点击事件绑定
            OnBtnClickBindEvent();
            
            //爱心重新更新显示
            for (int i = _heartCount; i > 0; i--)
            {
                ui.HeartsPanel.GetChild($"xin_{i}").visible = true;
            }
            
            // ui.ch_progress.value = DataMgr.ArrowChProgress.Value;
            
            _currentTipArrow = null;
            
            _zoomSlider = ui.com_bottom.zoomSlide;
            // 2. 绑定滑动条拖动回调
            _zoomSlider.onChanged.Add(OnZoomSliderChange);
            // 3. 容器初始缩放、位置
            // _viewContainer.scaleX = 1f;
            // _viewContainer.scaleY = 1f;
            // // 滑动条初始居中（对应 1.0 缩放）
            // float initValue = GetSliderValueByScale(1f);
            // _zoomSlider.value = initValue;
            // 先兜底重置为默认锚点
            _viewContainer.pivot = new Vector2(0, 0);

            if (_levelConfig == null) 
            {
                _initComplete = true;
                return;
            }
            
            // 第一步：绘制网格 → 内部会计算网格中心、修改pivot为网格中心
            DrawGridDots(_levelConfig);
            
            // ===== 新增：计算并应用初始缩放 =====
            float initialScale = CalculateInitialScale();
            _viewContainer.scaleX = initialScale;
            _viewContainer.scaleY = initialScale;
            
            // 同步滑动条位置（假设滑动条映射范围为 _minScale~_maxScale）
            float sliderValue = GetSliderValueByScale(initialScale);
            _zoomSlider.value = Mathf.Clamp(sliderValue, 0f, 100f); // 确保不越界

            // ========== 核心：把网格中心钉在屏幕中心 ==========
            // _gridWorldCenter 是 DrawGridDots 里 CalculateGridCenter 算出的值
            float screenCenterX = ui.view_container_parent.width * 0.5f;
            float screenCenterY = ui.view_container_parent.height * 0.5f;

            // 计算容器需要移动多少，才能让网格中心正好对准屏幕中心
            float targetContainerX = screenCenterX - _gridWorldCenter.x;
            float targetContainerY = screenCenterY - _gridWorldCenter.y;

            // 直接设置容器位置
            _viewContainer.SetPosition(targetContainerX, targetContainerY, 0);
            // ==================================================
            
            foreach (var arrow in _levelConfig.arrows)
            {
                BuildArrowPath(arrow, _levelConfig);
                CreateArrowAndLines(arrow, _levelConfig);
                MarkArrowOccupiedCells(arrow, _levelConfig);
            }
    
            _initComplete = true;

            SetModel();

            SetTopCurr();

            Set101();
            
            RefreshGift();
            
            _fingerTipObj = com_finger.CreateInstance();
            _viewContainer.AddChild(_fingerTipObj);
            _fingerTipObj.visible = false;
            _fingerTipObj.touchable = false;
            _fingerTipObj.sortingOrder = 9999;
            
            
            if (GameHelper.IsShowOpenGameUI() && HallManager.Instance.openTipsTimes >= 1)
            {
                UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.OpenGameUI_Open);
            }

        }
        
        private float CalculateInitialScale()
        {
            if (_levelConfig == null) return 0.8f; // 保底

            int cols = _levelConfig.gridCols;
            int rows = _levelConfig.gridRows;
            float spacing = _levelConfig.pointSpacing;

            // 网格实际像素宽高
            float gridWidth = (cols - 1) * spacing;
            float gridHeight = (rows - 1) * spacing;

            // 容器有效尺寸（可减去固定边距，如 40px）
            float margin = 80f;
            float containerWidth = ui.view_container_parent.width - margin * 2;
            float containerHeight = ui.view_container_parent.height - margin * 2;

            // 边界保护（网格可能只有一行/列）
            if (gridWidth <= 0) gridWidth = 1f;
            if (gridHeight <= 0) gridHeight = 1f;

            float scaleX = containerWidth / gridWidth;
            float scaleY = containerHeight / gridHeight;

            float scale = Mathf.Min(scaleX, scaleY);

            // 限制上限（避免网格过大时缩放太小；下限由滑动条范围决定，建议调整 _minScale）
            scale = Mathf.Clamp(scale, _minScale, _maxScale); // 下限设 0.3 更合理
            return scale;
        }
        
        private void SetTopCurr(object a = null)
        {
            if (ui.com_money is com_money btnMoney)
            {
                btnMoney.text_gold.text = DataMgr.Ticket.Value.ToString("0.00");
                btnMoney.SetClick(PopMakeup);
            }
        }

        private void PopMakeup()
        {
            var makeupTaskData = DataMgr.MakeupTaskHistory.Value.Last();
            var vo = ConfigSystem.GetConfig<MakeupModel>().GetData(makeupTaskData.tableId);
            if (vo == null)
            {
                Debug.LogError("MakeupTaskData 为空");    
                return;
            }
            GameHelper.showGameUI = false;
            uiCtrlDispatcher.Dispatch(UICtrlMsg.MakeupConfirmUI_Open, makeupTaskData);
        }

        #endregion

        #region UI相关
        
        private void OnBtnClickBindEvent()
        {
            ui.btn_close.SetClick(CtrlCloseUI);
            ui.com_bottom.btn_clear.SetClick(OnClickDeleteItem);
            ui.com_bottom.btn_hint.SetClick(OnClickHint);
            
            ui.com_bottom.btn_setting.SetClick(() =>
            {
                uiCtrlDispatcher.Dispatch(UICtrlMsg.PersonViewUI_Open, _moveSpeed);
            });
            
            if (!GameHelper.IsGiftSwitch() && ConfigSystem.GetConfig<CommonModel>().WVswitch == 0)
            {
                ui.btn_wv.visible = false;
            }
            ui.btn_wv.SetClick(() =>
            {
                SDKOpenConfig openConfig = new SDKOpenConfig
                {
                    normal = true,
                    url = ""
                };
                CtrlDispatcher.Instance.Dispatch(CtrlMsg.open_wb,openConfig);
            });
            
            ui.com_bottom.btn_skin.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.ArrowThemeUI_Open); });
            
            ui.btn_petty.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.PettyAwardUI_Open); });
            
            ui.btn_signin.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.SignInUI_Open); });
            
            ui.btn_statement.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.StatementViewUI_Open); });
            
            ui.btn_saveingpot.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.SaveingPotUI_Open); });
            
            ui.com_gift.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.SaveingPotUI_Open); });
        }
        

        // 缓存上一次箭头主题，用于判断是否需要重新随机彩色
        private int _lastArrowTheme = -1;
        /// <summary>
        /// 主题变更统一回调（昼夜/箭头主题切换都会触发）
        /// </summary>
        private void SetModel(object a = null)
        {
            int darkThemeIdx = DataMgr.ArrowDarkTheme.Value;
            int currentArrowTheme = DataMgr.ArrowTheme.Value;
    
            ui.mode.selectedIndex = darkThemeIdx;
    
            // 1. 更新网格圆点昼夜配色
            bool isDarkMode = darkThemeIdx == 0; // 0=黑夜
            Debug.Log($"ArrowDarkTheme==={darkThemeIdx}");
            UpdateGridDotColor(isDarkMode);

            // 2. 判断是否需要重新随机彩色：只有从非彩色切到彩色时才刷新
            bool forceRandomColorful = currentArrowTheme == 2 && _lastArrowTheme != currentArrowTheme;
    
            // 3. 更新所有箭头颜色
            UpdateAllArrowsColor(forceRandomColorful);
    
            // 4. 更新缓存，用于下次对比
            _lastArrowTheme = currentArrowTheme;
    
            Debug.Log($"主题刷新完成：昼夜={darkThemeIdx}，箭头主题={currentArrowTheme}");
        }
        /// <summary>
        /// 批量设置网格圆点颜色（随昼夜模式切换）
        /// // 色值映射：白天#2c3a62，黑夜#d4e3f2
        /// </summary>
        /// <param name="isDay">true=黑夜模式，false=白天模式</param>
        
        private string DarkColor = "#d4e3f2";
        private string LightColor = "#2c3a62";
        private void UpdateGridDotColor(bool isDay)
        {
            if (_gridDotList == null || _gridDotList.Count == 0)
                return;

            
            Debug.Log($"isDay=========={isDay}");
            string colorHex = isDay ? DarkColor : LightColor;
            ColorUtility.TryParseHtmlString(colorHex, out Color targetColor);

            foreach (var dot in _gridDotList)
            {
                if (dot == null) continue;
                
                dot.point.color = targetColor;
            }
        }
        
        /// <summary>
        /// 根据当前主题，为单个箭头生成对应颜色
        /// </summary>
        /// <param name="arrow">箭头配置对象</param>
        /// <param name="isRandomNew">彩色模式下是否重新随机颜色</param>
        private Color GetArrowRuntimeColor(ArrowConfig arrow, bool isRandomNew = false)
        {
            int themeType = DataMgr.ArrowTheme.Value;

            // 0 = 纯色模式（黑色按钮）：白天黑、黑夜白
            if (themeType == 0)
            {
                bool isDark = DataMgr.ArrowDarkTheme.Value == 0; // 0=黑夜
                return isDark ? Color.white : Color.black;
            }
            // 2 = 彩色随机模式
            else if (themeType == 2)
            {
                if (isRandomNew || arrow.runtimeColor == default)
                {
                    int randomIndex = Random.Range(0, _colorfulPalette.Length);
                    arrow.runtimeColor = _colorfulPalette[randomIndex];
                }
                return arrow.runtimeColor;
            }

            // 兜底默认黑色
            return Color.black;
        }
        
        /// 批量更新场上所有箭头的颜色（线条+单箭头）
        /// </summary>
        /// <param name="forceRandomColorful">彩色模式下是否强制重新随机</param>
        private void UpdateAllArrowsColor(bool forceRandomColorful = false)
        {
            if (_levelConfig == null || _levelConfig.arrows == null)
                return;

            int themeType = DataMgr.ArrowTheme.Value;
            bool isColorfulMode = themeType == 2;

            foreach (var arrow in _levelConfig.arrows)
            {
                // 只有强制刷新时，彩色模式才重新随机颜色
                bool isRandomNew = isColorfulMode && forceRandomColorful;
                Color targetColor = GetArrowRuntimeColor(arrow, isRandomNew);

                // 更新线条颜色
                if (arrow.lineUnits != null)
                {
                    foreach (var line in arrow.lineUnits)
                    {
                        if (line != null)
                        {
                            line.GetChild("line").asGraph.color = targetColor;
                        }
                    }
                }

                // 更新单箭头颜色
                if (arrow.arrowObj != null)
                {
                    GImage icon = arrow.arrowObj.GetChild("icon").asImage;
                    if (icon != null)
                    {
                        icon.color = targetColor;
                    }
                }
            }
        }
        
        //反馈特效工具
        /// <summary>
        /// 触发震动反馈
        /// </summary>
        /// <param name="isStrong">true=强震动（阻挡时用），false=轻震动（普通点击）</param>
        private void TriggerVibration(bool isStrong = false)
        {
            // 编辑器模式跳过震动
#if UNITY_EDITOR
            return;
#endif

            // 通用震动（兼容iOS/安卓，系统默认时长）
            // Handheld.Vibrate();

            Debug.Log($"震动反馈：{isStrong}");
            // ===== 进阶：安卓自定义震动时长（可选开启）=====
            if (Application.platform == RuntimePlatform.Android)
            {
                long duration = isStrong ? 100L : 50L;
                AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
                AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
                AndroidJavaObject vibrator = activity.Call<AndroidJavaObject>("getSystemService", "vibrator");
                vibrator.Call("vibrate", duration);
            }
        }

        /// <summary>
        /// 触发阻挡时两侧红色闪烁效果
        /// </summary>
        private void TriggerBlockFlash()
        {
            ui.t1.Play();
            
            // 先终止正在播放的动画，避免叠加错乱
            ui.com_ficker.visible = true;
            ui.com_ficker.t0.Play(() =>
            {
                ui.com_ficker.visible = false;
            });
            
        }
        
        #endregion

        private void InitGridBounds()
        {
            // 第0列、第0行的起点坐标
            gridLeft = ui.point.x;
            gridTop = ui.point.y;

            // 正确计算：(点数 - 1) × 点间距 = 总跨度
            gridRight = ui.point.x + (_levelConfig.gridCols - 1) * _levelConfig.pointSpacing;
            gridBottom = ui.point.y + (_levelConfig.gridRows - 1) * _levelConfig.pointSpacing;
        }

        /// <summary>初始化网格占用二维数组</summary>
        private void InitGridOccupied()
        {
            int rows = _levelConfig.gridRows;
            int cols = _levelConfig.gridCols;
            _gridOccupied = new bool[rows, cols];
            // 初始全部置为空闲
            for (int r = 0; r < rows; r++)
                for (int c = 0; c < cols; c++)
                    _gridOccupied[r, c] = false;

            _arrowOccupiedCells.Clear();
        }

        /// <summary>标记单个箭头占用的所有网格点</summary>
        private void MarkArrowOccupiedCells(ArrowConfig arrow, LevelConfig cfg)
        {
            List<(int row, int col)> cellList = new List<(int row, int col)>();
            int totalCol = cfg.gridCols;

            // 遍历箭头整条路径的所有网格点位
            foreach (var pos in arrow.pathPointList)
            {
                int pointId = GetPointIdByPos(pos, cfg);
                PointIdToRowCol(pointId, totalCol, out int r, out int c);

                // 边界保护
                if (r >= 0 && r < cfg.gridRows && c >= 0 && c < cfg.gridCols)
                {
                    _gridOccupied[r, c] = true;
                    cellList.Add((r, c));
                }
            }

            _arrowOccupiedCells[arrow] = cellList;
        }

        /// <summary>释放单个箭头占用的网格（箭头消失时调用）</summary>
        private void ReleaseArrowOccupied(ArrowConfig arrow)
        {
            if (!_arrowOccupiedCells.TryGetValue(arrow, out var cellList))
                return;

            foreach (var (r, c) in cellList)
            {
                if (r >= 0 && r < _levelConfig.gridRows && c >= 0 && c < _levelConfig.gridCols)
                {
                    _gridOccupied[r, c] = false;
                }
            }
            // 移除字典记录，避免冗余
            _arrowOccupiedCells.Remove(arrow);
        }

        #region 配置加载
        private void LoadLevelConfig()
        {
            // _levelConfig = JsonHelper.LoadLevel("level_1001.json");
            // 示例：加载第1关 levelId=0
            int currentLevelId = GameHelper.GetLevel();
            
            // 替换为：按ID加载单关
            bool loadSuccess = LevelManager.Instance.LoadLevel(currentLevelId);
            if (!loadSuccess)
            {
                // 加载失败兜底（比如回到第一关）
                return;
            }

            // 直接取当前关卡配置，后续逻辑完全不变
            _levelConfig = LevelManager.Instance.CurrentLevel;
            
            // if (_allLevelData.levels.Count < currentLevelId) currentLevelId = _allLevelData.levels.Count;
            // _levelConfig = LoadLevelById(currentLevelId);
            // // 给 path 为空的箭头，设置默认方向
            // foreach (var arrow in _levelConfig.arrows)
            // {
            //     if (arrow.path == null || arrow.path.Count == 0)
            //     {
            //         arrow.defaultDir = "up";
            //     }
            //     else
            //     {
            //         arrow.defaultDir = null;
            //     }
            // }
        }
        
        /// <summary>
        /// 从 all_levels.json 中按 levelId 读取指定关卡
        /// </summary>
        /// <param name="targetLevelId">目标关卡ID 如 1001</param>
        /// <returns>找到返回关卡数据，没找到返回null</returns>
        ///
        private static AllLevelRoot _allLevelData;
        // 游戏启动时调用一次
        public static void InitAllLevelData()
        {
            string path = Path.Combine(Application.streamingAssetsPath, "all_levels.json");
#if UNITY_ANDROID && !UNITY_EDITOR
        // Android 平台需要使用 UnityWebRequest 读取 StreamingAssets
        using (UnityEngine.Networking.UnityWebRequest www = UnityEngine.Networking.UnityWebRequest.Get(path))
        {
            www.SendWebRequest();
            while (!www.isDone) { }
            
            if (www.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
            {
                string json = www.downloadHandler.text;
                _allLevelData = JsonUtility.FromJson<AllLevelRoot>(json);
            }
            else
            {
                Debug.LogError($"Failed to load all_levels.json: {www.error}");
            }
        }
#else
            // Editor 和其他平台直接读取
            string json = File.ReadAllText(path);
            _allLevelData = JsonUtility.FromJson<AllLevelRoot>(json);
#endif
        
        }
        
        public static LevelConfig LoadLevelById(int targetLevelId)
        {
            AllLevelRoot root = _allLevelData;
            if (root == null || root.levels == null) return null;

            // 遍历查找对应关卡
            foreach (var lv in _allLevelData.levels)
            {
                if (lv.levelId == targetLevelId)
                {
                    return lv;
                }
            }
            Debug.LogWarning($"未找到关卡ID: {targetLevelId}");
            return null;
        }
        #endregion

        #region 【核心】网格坐标工具
        /// <summary>点位ID 转 行列索引</summary>
        private void PointIdToRowCol(int pointId, int totalCol, out int row, out int col)
        {
            row = pointId / totalCol;
            col = pointId % totalCol;
        }

        /// <summary>行列 → 实际像素坐标</summary>
        private Vector2 RowColToPos(int row, int col)
        {
            float originX = ui.point.x;
            float originY = ui.point.y;
            float x = originX + col * _levelConfig.pointSpacing;
            float y = originY + row * _levelConfig.pointSpacing;
            return new Vector2(x, y);
        }

        /// <summary>点位ID → 最终像素坐标</summary>
        private Vector2 PointIdToPos(int pointId, int totalCol)
        {
            PointIdToRowCol(pointId, totalCol, out int r, out int c);
            return RowColToPos(r, c);
        }

        /// <summary>像素坐标反向计算点位ID</summary>
        private int GetPointIdByPos(Vector2 pos, LevelConfig cfg)
        {
            float originX = ui.point.x;
            float originY = ui.point.y;

            int col = Mathf.RoundToInt((pos.x - originX) / cfg.pointSpacing);
            int row = Mathf.RoundToInt((pos.y - originY) / cfg.pointSpacing);

            return row * cfg.gridCols + col;
        }

        /// <summary> 根据FGUI旋转角度获取方向 </summary>
        private string GetDirByRotation(float rot)
        {
            rot = Mathf.Round(rot);
            if (rot == 0) return "right";
            if (rot == 90) return "down";
            if (rot == 180) return "left";
            if (rot == 270) return "up";
            return "right";
        }

        /// <summary>方向转旋转角度（箭头默认朝上）</summary>
        private float DirToRotation(string dir)
        {
            return dir switch
            {
                "up" => 0f,
                "right" => 90f,
                "down" => 180f,
                "left" => 270f,
                _ => 0f
            };
        }
        #endregion

        #region 绘制网格圆点
        private void DrawGridDots(LevelConfig cfg)
        {
            var rows = cfg.gridRows;
            var cols = cfg.gridCols;

            for (var r = 0; r < rows; r++)
            {
                for (var c = 0; c < cols; c++)
                {
                    var dot = ArrorPoint.CreateInstance();
                    var pos = RowColToPos(r, c);
                    dot.SetPosition(pos.x, pos.y, 0);
                    _viewContainer.AddChild(dot);
                    // 加入列表统一管理
                    _gridDotList.Add(dot);
                    
                }
            }
            
            // ========= 网格绘制完成后，设置居中缩放轴心 =========
            CalculateGridCenter(cfg);
            SetViewPivotToGridCenter();
        }
        
        /// <summary>计算整个网格圆点区域的世界中心点</summary>
        private void CalculateGridCenter(LevelConfig cfg)
        {
            int rows = cfg.gridRows;
            int cols = cfg.gridCols;

            // 取四个角点位，算出整体中心
            Vector2 topLeft = RowColToPos(0, 0);
            Vector2 bottomRight = RowColToPos(rows - 1, cols - 1);

            // 几何中心
            _gridWorldCenter.x = (topLeft.x + bottomRight.x) * 0.5f;
            _gridWorldCenter.y = (topLeft.y + bottomRight.y) * 0.5f;
        }
        
        /// <summary>设置_viewContainer缩放轴心 = 网格中心，实现居中缩放</summary>
        private void SetViewPivotToGridCenter()
        {
            if (_viewContainer == null) return;

            // FGUI pivot 取值范围 0~1，是容器内部相对比例
            float pivotX = _gridWorldCenter.x / _viewContainer.width;
            float pivotY = _gridWorldCenter.y / _viewContainer.height;

            // 限制在合法区间
            pivotX = Mathf.Clamp01(pivotX);
            pivotY = Mathf.Clamp01(pivotY);

            // 设置轴心，此后所有缩放都围绕网格中心
            _viewContainer.pivot = new Vector2(pivotX, pivotY);
        }
        #endregion

        #region 构建箭头路径点位
        private void BuildArrowPath(ArrowConfig arrow, LevelConfig cfg)
        {
            arrow.pathPointList = new List<Vector2>();
            int totalCol = cfg.gridCols;

            int curPointId = arrow.startPoint;
            Vector2 curPos = PointIdToPos(curPointId, totalCol);
            arrow.pathPointList.Add(curPos);

            foreach (string dir in arrow.path)
            {
                PointIdToRowCol(curPointId, totalCol, out int r, out int c);
                switch (dir)
                {
                    case "up": r--; break;
                    case "down": r++; break;
                    case "left": c--; break;
                    case "right": c++; break;
                }

                curPointId = r * totalCol + c;
                curPos = PointIdToPos(curPointId, totalCol);
                arrow.pathPointList.Add(curPos);
            }
        }
        #endregion

        #region 创建箭头 + 线条方块
        private void CreateArrowAndLines(ArrowConfig arrow, LevelConfig cfg)
        {
            arrow.lineUnits = new List<GComponent>();
            arrow.originalLinePos = new List<Vector2>();
            arrow.lineTrackIndex = new List<int>();
            arrow.totalTrack = new List<Vector2>();
            List<Vector2> pathPoints = arrow.pathPointList;

            // 生成整条连续行走轨迹
            if (pathPoints.Count >= 2)
            {
                for (int i = 0; i < pathPoints.Count - 1; i++)
                {
                    Vector2 segStart = pathPoints[i];
                    string dir = arrow.path[i];
                    float offX = 0, offY = 0;
                    switch (dir)
                    {
                        case "right": offX = 10; break;
                        case "left": offX = -10; break;
                        case "down": offY = 10; break;
                        case "up": offY = -10; break;
                    }

                    Vector2 cur = segStart;
                    for (int k = 0; k < 6; k++)
                    {
                        arrow.totalTrack.Add(cur);
                        cur.x += offX;
                        cur.y += offY;
                    }
                }

                if (arrow.path.Count > 0)
                    arrow.finalMoveDir = arrow.path[arrow.path.Count - 1];
            }

            // 路径只有单个点（纯箭头）
            if (pathPoints.Count < 2)
            {
                CreateArrowOnly(arrow, pathPoints[0]);
                // ========= 修复1：单点箭头也初始化轨迹索引 =========
                arrow.arrowTrackIndex = 0;
                arrow.finalMoveDir = GetDirByRotation(arrow.arrowObj.rotation);
                // ========= 修复2：强制记录原始坐标（关键！）=========
                arrow.originalArrowPos = new Vector2(arrow.arrowObj.x, arrow.arrowObj.y);
                // 优化：给单点箭头填充一个轨迹点，和有线箭头逻辑完全一致
                arrow.totalTrack.Add(pathPoints[0]); 
                return;
            }

            // 生成线条图块
            List<Vector2> tilePositions = new List<Vector2>();
            for (int i = 0; i < pathPoints.Count - 1; i++)
            {
                Vector2 segStart = pathPoints[i];
                string dir = arrow.path[i];
                float offX = 0, offY = 0;
                switch (dir)
                {
                    case "right": offX = 10; break;
                    case "left": offX = -10; break;
                    case "down": offY = 10; break;
                    case "up": offY = -10; break;
                }

                Vector2 cur = segStart;
                for (int k = 0; k < 6; k++)
                {
                    tilePositions.Add(cur);
                    cur.x += offX;
                    cur.y += offY;
                }
            }

            if (tilePositions.Count > 0)
                tilePositions.RemoveAt(tilePositions.Count - 1);

            for (int i = 0; i < tilePositions.Count; i++)
            {
                Vector2 pos = tilePositions[i];
                LineTile unit = LineTile.CreateInstance();
                unit.SetPosition(pos.x, pos.y, 0);
                // 运行时主题取色
                Color lineColor = GetArrowRuntimeColor(arrow);
                unit.GetChild("line").asGraph.color = lineColor;
                unit.visible = true;
                unit.touchable = true;
                unit.name = "Arrow_" + arrow.id;
                unit.onClick.Add(() => OnPathClick(arrow));

                _viewContainer.AddChild(unit);
                arrow.lineUnits.Add(unit);
                arrow.originalLinePos.Add(pos);
                arrow.lineTrackIndex.Add(i);
            }

            // 创建箭头
            CreateArrowOnly(arrow, pathPoints[pathPoints.Count - 1]);
            arrow.originalArrowPos = new Vector2(arrow.arrowObj.x, arrow.arrowObj.y);
            arrow.arrowTrackIndex = arrow.totalTrack.Count - 1;
            
            
            
            // ========== 初始化距离偏移 ==========
            arrow.movedDistance = 0f;
            // 轨迹总长度 = (点数-1) * 点间距
            arrow.trackTotalLength = (arrow.totalTrack.Count - 1) * 10f;
            // 箭头初始在轨迹末端
            arrow.arrowInitOffset = arrow.trackTotalLength;

            // 每个线条的初始偏移 = 索引 * 点间距
            arrow.lineInitOffsets = new List<float>();
            for (int i = 0; i < arrow.lineUnits.Count; i++)
            {
                arrow.lineInitOffsets.Add(i * 10f);
            }
        }
        #endregion

        
        #region 道具（删除和提示）
        private void SetUserPorp(object a)
        {
          
            if (a != null)
            {
                var type = (int)a;
                Debug.Log($"Set User Prop: {type}");
                if (type == 0)
                {
                    // 提示道具
                    ShowRandomArrowTip();
                }
                else if (type == 1)
                {
                    _isDeleteMode = !_isDeleteMode;
                    GameHelper.ShowTips("You can delete any one of the arrows!");
                }
            }
           
        }
        /// <summary>点击删除道具，开启/关闭删除模式</summary>
        private void OnClickDeleteItem()
        {
            // _isDeleteMode = !_isDeleteMode;
    
            UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.ArrowTipsUI_Open, 1);
            
            // // 可选：加提示，区分状态
            // if (_isDeleteMode)
            // {
            //     Debug.Log("已开启删除模式，点击箭头即可删除");
            //     // 可加UI提示：如图标高亮、文字提示
            // }
            // else
            // {
            //     Debug.Log("已关闭删除模式");
            // }
        }
        
        private void OnClickHint()
        {
            UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.ArrowTipsUI_Open, 0);
        }
        
        /// <summary>随机选中有效箭头，显示静态手指提示（不跟随移动）</summary>
        private void ShowRandomArrowTip()
        {
            // 先隐藏上一次提示
            HideFingerTip();

            if (_levelConfig == null || _levelConfig.arrows == null || _levelConfig.arrows.Count == 0)
            {
                Debug.Log("暂无可提示的箭头");
                return;
            }

            // 筛选条件：对象存在 + 未销毁 + 未在移动中 + 前方无阻挡可直接消除
            List<ArrowConfig> validArrows = _levelConfig.arrows
                .Where(arrow => 
                    arrow != null && 
                    arrow.arrowObj != null && 
                    !arrow.isMoving &&
                    !IsArrowBlocked(arrow))
                .ToList();

            if (validArrows.Count == 0)
            {
                Debug.Log("当前没有可直接消除的箭头，无法提示");
                return;
            }

            // 从可消除箭头中随机选一个
            int randomIndex = Random.Range(0, validArrows.Count);
            _currentTipArrow = validArrows[randomIndex];

            // 设置手指提示位置
            float x = _currentTipArrow.arrowObj.x + _fingerOffset.x;
            float y = _currentTipArrow.arrowObj.y + _fingerOffset.y;
            _fingerTipObj.SetPosition(x, y, 0);
            _fingerTipObj.visible = true;
        }

        /// <summary>隐藏手指提示并清空标记</summary>
        private void HideFingerTip()
        {
            if (_fingerTipObj != null)
            {
                _fingerTipObj.visible = false;
            }
            _currentTipArrow = null;
        }
        #endregion


        #region 缩放

        /// <summary>
        /// 判断FGUI全局坐标是否落在拖拽有效区域（view_container_parent）内
        /// </summary>
        private bool IsInDragArea(Vector2 fguiGlobalPos)
        {
            if (ui.view_container_parent == null)
                return false;

            // 全局坐标转成父容器本地坐标（左上角为原点）
            Vector2 localPos = ui.view_container_parent.GlobalToLocal(fguiGlobalPos);
    
            // 判断是否在容器矩形范围内
            return localPos.x >= 0 
                   && localPos.x <= ui.view_container_parent.width 
                   && localPos.y >= 0 
                   && localPos.y <= ui.view_container_parent.height;
        }
        /// <summary>
        /// 判断FGUI全局坐标是否落在圆点网格范围内
        /// </summary>
        private bool IsInGridArea(Vector2 fguiGlobalPos)
        {
            if (_viewContainer == null || _levelConfig == null)
                return false;

            // 全局坐标转成 _viewContainer 本地坐标（网格坐标基于该容器）
            Vector2 localPos = _viewContainer.GlobalToLocal(fguiGlobalPos);

            // 和网格四边边界比对
            return localPos.x >= gridLeft
                   && localPos.x <= gridRight
                   && localPos.y >= gridTop
                   && localPos.y <= gridBottom;
        }
        
        /// <summary>根据缩放值，计算滑动条 value(0~1)</summary>
        private float GetSliderValueByScale(float scale)
        {
            float validScale = Mathf.Clamp(scale, _minScale, _maxScale);
            float ratio = (validScale - _minScale) / (_maxScale - _minScale);
            float sliderVal = ratio * 100f;
    
            return Mathf.Clamp(sliderVal, 0f, 100f);
        }

        /// <summary>根据滑动条 value(0~1)，计算实际缩放值</summary>
        private float GetScaleBySliderValue(float sliderValue)
        {
            // 先把滑块值钳位在 0~100 内
            float val = Mathf.Clamp(sliderValue, 0f, 100f);
    
            // 线性映射：0→0.6，100→1.5
            float ratio = val / 100f;
            float scale = _minScale + ratio * (_maxScale - _minScale);
    
            return Mathf.Clamp(scale, _minScale, _maxScale);
        }

        /// <summary>滑动条数值变化回调</summary>
        private void OnZoomSliderChange()
        {
            float scale = GetScaleBySliderValue((float)_zoomSlider.value);
            _viewContainer.scaleX = scale;
            _viewContainer.scaleY = scale;
        }
        
        /// <summary>移动容器 + 边界限制，防止完全拖出屏幕</summary>
        private void MoveContainer(Vector2 offset)
        {
            Debug.Log($"[arrow] 移动容器：{offset}");
            float newX = _viewContainer.x + offset.x;
            float newY = _viewContainer.y + offset.y;

            float viewW = _viewContainer.width * _viewContainer.scaleX;
            float viewH = _viewContainer.height * _viewContainer.scaleY;

            // 左右边界
            float minX = -viewW + _dragBorder;
            float maxX = Screen.width - _dragBorder;
            newX = Mathf.Clamp(newX, minX, maxX);

            // 上下边界
            float minY = -viewH + _dragBorder;
            float maxY = Screen.height - _dragBorder;
            newY = Mathf.Clamp(newY, minY, maxY);

            _viewContainer.SetPosition(newX, newY,0);
        }
        
        #endregion
        
        
        #region 点击 & 移动逻辑
        
        // 线条/箭头 统一点击回调
        private void OnPathClick(ArrowConfig clickArrow)
        {
            if (clickArrow.isMoving)
            {
                Debug.Log($"[arrow] {clickArrow.id}正在移动，请勿点击其他箭头");
                return;
            }
            
            TriggerVibration(true);
            
            
            // 点击的是当前提示箭头，直接隐藏手指
            if (_currentTipArrow == clickArrow)
            {
                HideFingerTip();
            }
            
            // ========= 删除模式：直接删除箭头，不走移动逻辑 =========
            if (_isDeleteMode)
            {
                DeleteArrowCompletely(clickArrow);
                return;
            }

            clickArrow.isMoving = true;
            bool hasBlock = CheckBlockByGrid(clickArrow);
            Debug.Log("[arrow] 点击箭头 id：" + clickArrow.id +$" hasBlock=阻挡=={hasBlock}");
            if (hasBlock)
            {
                MoveThenReset(clickArrow);
            }
            else
            {
                ReleaseArrowOccupied(clickArrow);
                MoveToDisappear(clickArrow);
            }
        }
        
        /// <summary>
        /// 纯检测：箭头前方是否有阻挡（无副作用，不扣血、不改状态）
        /// </summary>
        /// <returns>true=前方有阻挡，false=可直接飞出消除</returns>
        private bool IsArrowBlocked(ArrowConfig curArrow)
        {
            if (_gridOccupied == null || _levelConfig == null) 
                return false;

            // 获取箭头头部所在网格行列
            Vector2 arrowPos = new Vector2(curArrow.arrowObj.x, curArrow.arrowObj.y);
            int curPointId = GetPointIdByPos(arrowPos, _levelConfig);
            PointIdToRowCol(curPointId, _levelConfig.gridCols, out int curRow, out int curCol);

            // 箭头已超出网格，判定无阻挡
            if (curRow < 0 || curRow >= _levelConfig.gridRows || curCol < 0 || curCol >= _levelConfig.gridCols)
                return false;

            // 获取最终射出方向
            string moveDir;
            if (curArrow.path != null && curArrow.path.Count > 0)
            {
                moveDir = curArrow.path[curArrow.path.Count - 1];
            }
            else if (!string.IsNullOrEmpty(curArrow.defaultDir))
            {
                moveDir = curArrow.defaultDir;
            }
            else
            {
                moveDir = "up";
            }

            // 沿射出方向遍历前方网格
            switch (moveDir)
            {
                case "up":
                    for (int r = curRow - 1; r >= 0; r--)
                    {
                        if (_gridOccupied[r, curCol])
                            return true;
                    }
                    break;
                case "down":
                    for (int r = curRow + 1; r < _levelConfig.gridRows; r++)
                    {
                        if (_gridOccupied[r, curCol])
                            return true;
                    }
                    break;
                case "left":
                    for (int c = curCol - 1; c >= 0; c--)
                    {
                        if (_gridOccupied[curRow, c])
                            return true;
                    }
                    break;
                case "right":
                    for (int c = curCol + 1; c < _levelConfig.gridCols; c++)
                    {
                        if (_gridOccupied[curRow, c])
                            return true;
                    }
                    break;
            }

            return false;
        }
        // 基于网格行列的阻挡检测（带扣血逻辑，点击箭头时调用）
        private bool CheckBlockByGrid(ArrowConfig curArrow)
        {
            bool isCollide = IsArrowBlocked(curArrow);

            // ========== 碰撞判重 + 扣爱心 ==========
            if (isCollide)
            {
                // 同个箭头第一次碰撞才扣血
                if (!_collidedArrows.Contains(curArrow))
                {
                    _collidedArrows.Add(curArrow);
                    _heartCount--;
            
                    if (_heartCount > 0) 
                        ui.HeartsPanel.GetChild($"xin_{_heartCount + 1}").visible = false;
            
                    // 爱心归零 → 失败结算
                    if (_heartCount <= 0)
                    {
                        ShowResult(false);
                    }
                }
            } 

            return isCollide;
        }
        
        /// <summary>
        /// 计算箭头沿射出方向，到前方第一个阻挡点的前移距离（像素）
        /// </summary>
        /// <param name="arrow">目标箭头</param>
        /// <param name="hitMargin">碰撞预留余量，离阻挡点还差多少像素就停下回弹</param>
        /// <returns>实际前移的总像素距离</returns>
        private float GetDistanceToFirstBlock(ArrowConfig arrow, float hitMargin = 20f)
        {
            if (_gridOccupied == null || _levelConfig == null)
                return 20f; // 异常兜底，保持原默认值

            // 计算箭头头部所在的网格坐标（路径终点）
            int curRow = arrow.startPoint / _levelConfig.gridCols;
            int curCol = arrow.startPoint % _levelConfig.gridCols;
    
            // 沿路径走到终点，定位箭头头部网格位置
            foreach (string dir in arrow.path)
            {
                switch (dir)
                {
                    case "up": curRow--; break;
                    case "down": curRow++; break;
                    case "left": curCol--; break;
                    case "right": curCol++; break;
                }
            }

            string moveDir = GetFinalMoveDir(arrow);
            int emptyGridCount = 0;

            // 沿射出方向逐格检测第一个阻挡
            while (true)
            {
                // 前进一步
                switch (moveDir)
                {
                    case "up": curRow--; break;
                    case "down": curRow++; break;
                    case "left": curCol--; break;
                    case "right": curCol++; break;
                }

                // 出界无阻挡，兜底返回默认距离
                if (curRow < 0 || curRow >= _levelConfig.gridRows 
                               || curCol < 0 || curCol >= _levelConfig.gridCols)
                {
                    return 20f;
                }

                // 找到阻挡点
                if (_gridOccupied[curRow, curCol])
                {
                    // 总距离 = 空格数*点间距 + (单格距离 - 预留余量)
                    float total = (emptyGridCount + 1) * _levelConfig.pointSpacing - hitMargin;
                    // 最小前移保护，避免阻挡太近时几乎没动画
                    return Mathf.Max(total, 6f);
                }

                emptyGridCount++;
            }
        }
        
        /// <summary>彻底删除指定箭头：销毁UI+清数据+停逻辑+移除列表</summary>
        private void DeleteArrowCompletely(ArrowConfig arrow)
        {
            if (arrow == null) return;
            
            // 被删除的是提示箭头，隐藏手指
            if (_currentTipArrow == arrow)
            {
                HideFingerTip();
            }
            
            // 1. 停止移动标记，拦截Update和协程
            arrow.isMoving = false;

            // 2. 销毁箭头中所有线条UI
            if (arrow.lineUnits != null)
            {
                foreach (var line in arrow.lineUnits)
                {
                    if (line != null)
                    {
                        _viewContainer.RemoveChild(line);
                        line.Dispose();
                    }
                }
                arrow.lineUnits.Clear();
            }

            // 3. 销毁箭头本体UI
            if (arrow.arrowObj != null)
            {
                _viewContainer.RemoveChild(arrow.arrowObj);
                arrow.arrowObj.Dispose();
                arrow.arrowObj = null;
            }

            // 4. 释放网格占用数据（和飞出边界逻辑保持一致）
            ReleaseArrowOccupied(arrow);

            // 5. 存活箭头计数-1
            _remainArrowCount--;

            // 6. 从总列表移除该箭头（核心：删除数据源）
            if (_levelConfig != null && _levelConfig.arrows != null)
            {
                _levelConfig.arrows.Remove(arrow);
            }

            // 7. 可选：判断是否全部删除完成，触发结算（按需开启）
            if (_remainArrowCount <= 0)
            {
                ShowResult(true);
            }
            else
            {
                // 消除每条线小概率触发
                DisappearLineGetReward();
            }
            // 8. 退出删除模式
            _isDeleteMode = false;
            Debug.Log("[arrow] 箭头已彻底删除");
        }

        // 创建箭头 + 绑定点击
        private void CreateArrowOnly(ArrowConfig arrow, Vector2 arrowPos)
        {
            ArrowEnd arrowIns = ArrowEnd.CreateInstance();
            arrowIns.SetPosition(arrowPos.x, arrowPos.y, 0);
            arrowIns.touchable = true;

            if (arrow.path != null && arrow.path.Count > 0)
            {
                string lastDir = arrow.path[arrow.path.Count - 1];
                arrowIns.rotation = DirToRotation(lastDir);
            }
            else if (!string.IsNullOrEmpty(arrow.defaultDir))
            {
                // 纯单点箭头 使用默认方向旋转
                arrowIns.rotation = DirToRotation(arrow.defaultDir);
            }

            // 运行时主题取色
            Color arrowColor = GetArrowRuntimeColor(arrow);
            GImage icon = arrowIns.GetChild("icon").asImage;
            if (icon != null) icon.color = arrowColor;

            arrowIns.visible = true;
            arrowIns.name = "Arrow_" + arrow.id;
            arrowIns.onClick.Add(() => OnPathClick(arrow));
            _viewContainer.AddChild(arrowIns);

            arrow.arrowObj = arrowIns;
            arrow.isMoving = false;
        }
        
        // 移动后复位动画（回弹，不释放占用）
        private void MoveThenReset(ArrowConfig arrow)
        {
            RunCoroutine(MoveAndResetCoroutine(arrow));
        }

        private readonly float _bounceSpeed = 1200f;
        private IEnumerator MoveAndResetCoroutine(ArrowConfig arrow)
        {
            arrow.movedDistance = 0f;
            arrow.finalMoveDir = GetFinalMoveDir(arrow);
    
            // 动态计算：前移到快碰到阻挡点的距离
            float forwardTotal = GetDistanceToFirstBlock(arrow, 3f);
            float duration = forwardTotal / _bounceSpeed;
            float timer = 0f;

            // ========== 第一段：向前移动到快碰到阻挡 ==========
            while (timer < duration)
            {
                if (arrow == null || arrow.arrowObj == null) yield break;
        
                timer += Time.deltaTime;
                arrow.movedDistance = Mathf.Min(_bounceSpeed * timer, forwardTotal);
        
                UpdateAllElementsPosition(arrow);
                yield return null;
            }

            // 核心：阻挡强震动 + 红色氛围闪烁
            TriggerBlockFlash();
            
            // 强制对齐前移终点，消除浮点误差
            arrow.movedDistance = forwardTotal;
            UpdateAllElementsPosition(arrow);

            // ========== 直接瞬间复位（删除原渐变回弹逻辑）==========
            // 可选：加 1 帧停顿，模拟碰撞卡顿感，觉得突兀就打开注释
            // yield return null;

            // 距离归零 + 原始坐标强制校准，双保险保证精准回到初始位置
            arrow.movedDistance = 0f;
            UpdateAllElementsPosition(arrow);
    
            if (arrow.arrowObj != null)
            {
                arrow.arrowObj.SetPosition(arrow.originalArrowPos.x, arrow.originalArrowPos.y, 0);
            }
            for (int i = 0; i < arrow.lineUnits.Count; i++)
            {
                if (arrow.lineUnits[i] != null && i < arrow.originalLinePos.Count)
                {
                    arrow.lineUnits[i].SetPosition(
                        arrow.originalLinePos[i].x, 
                        arrow.originalLinePos[i].y, 0);
                }
            }

            arrow.isMoving = false;
        }

        /// <summary>统一更新箭头+所有线条的位置</summary>
        private void UpdateAllElementsPosition(ArrowConfig arrow)
        {
            for (int i = 0; i < arrow.lineUnits.Count; i++)
            {
                float curDist = arrow.lineInitOffsets[i] + arrow.movedDistance;
                Vector2 pos = GetPositionByDistance(arrow, curDist);
                arrow.lineUnits[i].SetPosition(pos.x, pos.y, 0);
            }

            float arrowCurDist = arrow.arrowInitOffset + arrow.movedDistance;
            Vector2 arrowPos = GetPositionByDistance(arrow, arrowCurDist);
            arrow.arrowObj.SetPosition(arrowPos.x, arrowPos.y, 0);
        }

        private void MoveToDisappear(ArrowConfig arrow)
        {
            RunCoroutine(MoveAndDisappearCoroutine(arrow));
        }


        private void SetSpeed(object a = null)
        {
            if (a != null)
            {
                _moveSpeed = (float)a;
            }
        }
        
        // 新增：统一移动速度（像素/秒），数值越大移动越快，可自由修改
        [SerializeField] private float _moveSpeed = 1500f; 
        private IEnumerator MoveAndDisappearCoroutine(ArrowConfig arrow)
        {
            arrow.movedDistance = 0f;
            arrow.finalMoveDir = GetFinalMoveDir(arrow);

            while (true)
            {
                // 空对象兜底保护
                if (arrow == null || arrow.arrowObj == null)
                {
                    yield break;
                }

                // 每帧累计移动距离：速度 * 时间增量
                float deltaDis = _moveSpeed * Time.deltaTime;
                arrow.movedDistance += deltaDis;

                // 更新所有线条位置
                for (int i = 0; i < arrow.lineUnits.Count; i++)
                {
                    float curDist = arrow.lineInitOffsets[i] + arrow.movedDistance;
                    Vector2 pos = GetPositionByDistance(arrow, curDist);
                    arrow.lineUnits[i].SetPosition(pos.x, pos.y, 0);
                }

                // 更新箭头位置
                float arrowCurDist = arrow.arrowInitOffset + arrow.movedDistance;
                Vector2 arrowPos = GetPositionByDistance(arrow, arrowCurDist);
                arrow.arrowObj.SetPosition(arrowPos.x, arrowPos.y, 0);

                // 边界检测：以尾部线条为准（和原逻辑一致）
                Vector2 tailPos = GetPositionByDistance(arrow, arrow.movedDistance);
                if (CheckIsOutOfBounds(tailPos, arrow.finalMoveDir))
                {
                    // ========== 以下销毁逻辑和原代码保持一致 ==========
                    // ReleaseArrowOccupied(arrow);

                    foreach (var line in arrow.lineUnits)
                    {
                        if (line != null)
                        {
                            _viewContainer.RemoveChild(line);
                            line.Dispose();
                        }
                    }
                    arrow.lineUnits.Clear();

                    if (arrow.arrowObj != null)
                    {
                        _viewContainer.RemoveChild(arrow.arrowObj);
                        arrow.arrowObj.Dispose();
                        arrow.arrowObj = null;
                    }

                    arrow.isMoving = false;
                    _remainArrowCount--;

                    if (_remainArrowCount <= 0)
                    {
                        ShowResult(true);
                    }
                    else
                    {
                        DisappearLineGetReward();
                    }

                    yield break;
                }

                yield return null; // 每帧执行一次，平滑移动
            }
        }
        
        #region 移动工具方法
        /// <summary>根据距离，计算沿轨迹的实际坐标</summary>
        private Vector2 GetPositionByDistance(ArrowConfig arrow, float distance)
        {
            int trackCount = arrow.totalTrack.Count;
            const float segmentLen = 10f; // 轨迹点固定间距

            // 距离在轨迹范围内：两点插值得到精确位置
            if (distance <= arrow.trackTotalLength)
            {
                float indexFloat = distance / segmentLen;
                int index0 = Mathf.FloorToInt(indexFloat);
                int index1 = Mathf.Min(index0 + 1, trackCount - 1);
                float t = indexFloat - index0;

                Vector2 p0 = arrow.totalTrack[index0];
                Vector2 p1 = arrow.totalTrack[index1];
                return Vector2.Lerp(p0, p1, t);
            }
            // 距离超出轨迹：沿最终方向直线延伸
            else
            {
                float extraDis = distance - arrow.trackTotalLength;
                Vector2 endPos = arrow.totalTrack[trackCount - 1];
                Vector2 dir = DirToVector2(arrow.finalMoveDir);
                return endPos + dir * extraDis;
            }
        }

        /// <summary>方向字符串转单位向量</summary>
        /// <summary>方向字符串转单位向量（适配 FGUI 坐标系：y 轴向下）</summary>
        private Vector2 DirToVector2(string dir)
        {
            return dir switch
            {
                "up"    => new Vector2(0, -1),  // FGUI 向上 = y 减小
                "down"  => new Vector2(0, 1),   // FGUI 向下 = y 增大
                "left"  => new Vector2(-1, 0),
                "right" => new Vector2(1, 0),
                _ => new Vector2(0, -1)
            };
        }
        /// <summary>统一获取箭头最终移动方向</summary>
        private string GetFinalMoveDir(ArrowConfig arrow)
        {
            if (arrow.path != null && arrow.path.Count > 0)
                return arrow.path[arrow.path.Count - 1];
            if (!string.IsNullOrEmpty(arrow.defaultDir))
                return arrow.defaultDir;
            return "up";
        }

        /// <summary>统一边界检测</summary>
        private bool CheckIsOutOfBounds(Vector2 pos, string dir)
        {
            switch (dir)
            {
                case "right": return pos.x > gridRight + outOffset;
                case "left":  return pos.x < gridLeft - outOffset;
                case "down":  return pos.y > gridBottom + outOffset;
                case "up":    return pos.y < gridTop - outOffset;
                default: return false;
            }
        }
        #endregion
        
        /// <summary>
        /// 消除每条线时概率获得奖励
        /// </summary>
        private void DisappearLineGetReward()
        {
            return;
            if (!GameHelper.IsGiftSwitch()) return;
            
            bool isGet = false;
                
            int money_rate = ConfigSystem.GetConfig<CommonModel>().rewardrate;
            var randomNum = UnityEngine.Random.Range(0, 100);
            // Debug.Log($"[creat]  money_rate------------ {randomNum}==={money_rate}");
            if (randomNum < money_rate)
            {
                isGet = true;
                
                // 显示大额奖励
                float[] cash_array = GameHelper.GetRewardValue(1);
                var temp = new SuccessData();
                temp.IsWin = true;
                temp.cash_number = cash_array[0];
                temp.rate = (int)cash_array[1];
                temp.IsLevelSuccess = false;
                temp.IsH5Reward = false;
                temp.boost_array = GameHelper.GetRewardBoost(1);
                DOVirtual.DelayedCall(0.25f, () =>
                {
                    UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.LevelSuccessUI_Open, temp);
                });
            }
            
            
            if (!isGet && Random.Range(0, 100) < ConfigSystem.GetConfig<CommonModel>().Smallrewardsrate)
            {
                var rewardData = new RewardData();
                Vector2 fguiPosition = new Vector2(ui.samll_point.x, ui.samll_point.y);
                var start = fguiPosition;
                var end = GameHelper.GetUICenterPosition(ui.com_money.GetChild("text_gold"));
                float[] cash_array = GameHelper.GetRewardValue(0);
                Debug.Log($"小额奖励：{cash_array[0]}");
                var rewardSingleData = new RewardSingleData(102, (decimal)cash_array[0], RewardOrigin.AdTask)
                {
                    startPosition = start,
                    endPosition = new Vector2(end.x - 135, end.y - 135)
                };

                rewardData.AddReward(rewardSingleData);
                rewardData.displayType = RewardDisplayType.RewardFly | RewardDisplayType.ValueChange;
                GameDispatcher.Instance.Dispatch(GameMsg.GetReward, rewardData);         
            }
        }
        
        #endregion
       

        #region 资源清理
        
        /// <summary>全局启动协程 + 自动记录，关闭界面时统一停止</summary>
        private Coroutine RunCoroutine(IEnumerator cor)
        {
            if (cor == null) return null;
            Coroutine co = CrazyAsyKit.StartCoroutine(cor);
            if (co != null)
            {
                _activeCoroutines.Add(co);
            }
            return co;
        }
        
        private void ClearAllDynamicObj()
        {
            if (_levelConfig == null || _viewContainer == null) return;
            
            // 销毁网格圆点
            foreach (var dot in _gridDotList)
            {
                if (dot != null)
                {
                    _viewContainer.RemoveChild(dot);
                    dot.Dispose();
                }
            }
            _gridDotList.Clear();
            
            
            foreach (var arrow in _levelConfig.arrows)
            {
                if (arrow.arrowObj != null)
                {
                    _viewContainer.RemoveChild(arrow.arrowObj);
                    arrow.arrowObj.Dispose();
                    arrow.arrowObj = null;
                }

                foreach (var line in arrow.lineUnits)
                {
                    _viewContainer.RemoveChild(line);
                    line.Dispose();
                }
                arrow.lineUnits.Clear();
            }
        }
        #endregion

        #region 结算
        
        /// <summary>
        /// 弹出结算
        /// isSuccess: true=成功，false=失败
        /// </summary>
        private void ShowResult(bool isSuccess)
        {
            // 暂停所有交互/移动（可选）
            foreach (var arrow in _levelConfig.arrows)
            {
                arrow.isMoving = false;
            }
            
            // uiCtrlDispatcher.Dispatch(UICtrlMsg.LevelSuccessUI_Open, isSuccess);
            
            if (isSuccess)
            {
                GameHelper.SetLevel(GameHelper.GetLevel() + 1);

                // DataMgr.ArrowChProgress.Value += 10;
                //通关成功
                float[] cash_array = GameHelper.GetRewardValue(2);

                var temp = new SuccessData();
                temp.IsWin = true;
                temp.cash_number = cash_array[0];
                temp.rate = (int)cash_array[1];
                temp.IsLevelSuccess = true;
                temp.IsH5Reward = false;
                temp.boost_array = GameHelper.GetRewardBoost(2);
                DOVirtual.DelayedCall(0.2f, () =>
                {
                    UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.LevelSuccessUI_Open, temp);
                });
            }
            else
            {
                //通关失败
                float[] cash_array = GameHelper.GetRewardValue(2);
                
                var temp = new SuccessData();
                temp.IsWin = false;
                temp.cash_number = cash_array[0];
                temp.IsLevelSuccess = true;
                temp.IsH5Reward = false;
                temp.boost_array = GameHelper.GetRewardBoost(2);
                DOVirtual.DelayedCall(0.2f, () =>
                {
                    UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.LevelSuccessUI_Open, temp);
                });
            }
        }
        
        /// <summary>
        /// 重新开始本局
        /// </summary>
        /// <param name="a">true: 重生  false：重开一局或者下一局 </param>
        private void OnRestartGame(object a = null)
        {

            Debug.Log($"[ArrowGameUI] 重新开始本局  a:{(bool)a}");
            if (a != null && (bool)a)
            {
                _heartCount = 3;
                for (int i = _heartCount; i > 0; i--)
                {
                    ui.HeartsPanel.GetChild($"xin_{i}").visible = true;
                }
                return;
            }
            
            // 先清空当前所有动态物体
            ClearAllDynamicObj();
            
            //获取下一关的配置数据
            LoadLevelConfig();
            // 重新走初始化流程
            InitGridBounds();
            InitGridOccupied();
            _heartCount = 3;
            _collidedArrows.Clear();
            _remainArrowCount = _levelConfig.arrows.Count;
            DataMgr.ArrowResultLevel.Value = 1;
            
            InitView();
        }
        #endregion


    }

    #region 配置解析 & 数据结构
    public static class JsonHelper
    {
        public static LevelConfig LoadLevel(string fileName)
        {
            string path = Path.Combine(Application.streamingAssetsPath, fileName);
            return JsonUtility.FromJson<LevelConfig>(File.ReadAllText(path));
        }
    }

    [System.Serializable]
    public class LevelConfig
    {
        public int levelId;
        public string levelName;
        public int gridRows;
        public int gridCols;
        public int pointSpacing;
        public List<ArrowConfig> arrows;
    }

    [System.Serializable]
    public class ArrowConfig
    {
        public int id;
        public int startPoint;
        public List<string> path;
        [NonSerialized] public int endPoint;
        [NonSerialized] public string color;
        
        [NonSerialized]public string defaultDir;
        [NonSerialized]public List<Vector2> pathPointList;
        [NonSerialized]public List<GComponent> lineUnits;
        [NonSerialized]public GComponent arrowObj;

        [NonSerialized]public bool isMoving;
        [NonSerialized]public List<Vector2> originalLinePos;
        [NonSerialized]public Vector2 originalArrowPos;

        [NonSerialized]public List<Vector2> totalTrack;
        [NonSerialized]public List<int> lineTrackIndex;
        [NonSerialized]public int arrowTrackIndex;
        [NonSerialized]public string finalMoveDir;
        
        // ========== 距离驱动移动所需字段 ==========
        [NonSerialized]public float movedDistance;       // 当前整体累计移动距离
        [NonSerialized]public float trackTotalLength;    // 整条轨迹的总像素长度
        [NonSerialized]public List<float> lineInitOffsets; // 每个线条单元的初始距离偏移
        [NonSerialized]public float arrowInitOffset;     // 箭头的初始距离偏移
        // ========== 运行时颜色缓存 ==========
        [NonSerialized]public Color runtimeColor;
    }
    
    [System.Serializable]
    public class AllLevelRoot
    {
        public List<LevelConfig> levels;
    }
    #endregion
    
    public class SuccessData
    {
        public bool IsWin;
        public float cash_number;
        public bool IsLevelSuccess;
        public bool IsH5Reward;
        public int rate;
        public int[] boost_array;
    }

}