import {
  _decorator,
  Component,
  Node,
  Prefab,
  instantiate,
  Sprite,
  SpriteFrame,
  Vec2,
  Vec3,
  input,
  Input,
  EventTouch,
  UITransform,
  Size,
  Color,
  Graphics,
  Tween,
  tween,
  AudioClip,
  AudioSource,
  UIOpacity,
  resources,
} from "cc";
import { AudioManager, BGMType, SFXType } from "./AudioManager";
import { ButtonBadge } from "./ButtonBadge";
import { DragManager } from "./DragManager";
import { HintManager } from "./HintManager";
import { LevelBuilder } from "./LevelBuilder";
import { LevelManager } from "./LevelManager";
import { loadSpriteFrames } from "./utils/loadResources";
const { ccclass, property } = _decorator;

@ccclass("GameManager")
export class GameManager extends Component {
  @property(Prefab)
  blockPrefab: Prefab = null!;

  @property(Node)
  lineLayer: Node = null;

  @property(Node)
  Function_Node: Node = null;

  @property(AudioClip)
  remove_audio: AudioClip = null;

  @property(AudioClip)
  levle_audio: AudioClip = null;

  @property(AudioClip)
  victory_sfx_audio: AudioClip = null;

  @property(AudioClip)
  magic_sfx_audio: AudioClip = null;

  @property(AudioClip)
  hint_sfx_audio: AudioClip = null;

  @property(AudioClip)
  shuffle_sfx_audio: AudioClip = null;

  @property(HintManager)
  hintManager: HintManager | null = null;

  @property
  rows: number = 5;

  @property
  cols: number = 6;

  @property
  blockSize: number = 128;

  @property
  tileTypes: number = 5;

  @property
  level: number = 1; // 当前关卡

  private toolBadge: ButtonBadge | null = null;

  private board: number[][] = [];
  private blockNodes: (Node | null)[][] = [];
  private spriteFrames: SpriteFrame[] = [];

  private draggingBlock: Node | null = null;
  private dragStartPos: Vec2 = new Vec2();
  private dragStartBlockPos: Vec3 = new Vec3();
  private dragRow = -1;
  private dragCol = -1;
  private slidingDir: { dirR: number; dirC: number } | null = null;
  private threshold: number = 10; // 触发滑动阈值

  private hintPair: {
    from: { r?: number; c?: number };
    to: { r?: number; c?: number };
  } = { from: {}, to: {} };

  // 新增成员变量，标记可消除的方块集合
  private markedBlocks: Set<string> = new Set();
  private firstClicked: { r: number; c: number } | null = null;

  // 组合移动
  private draggingGroup: { r: number; c: number }[] = [];
  private dragManager: DragManager | null = null;

  private levelBuilder: LevelBuilder;

  @property
  currentShape: string = "rectangle";

  private currentShapeNode: Node = null!;

  private isTutorial: boolean = false;
  private tutorialStep: number = 0;
  private handNode: Node = null;

  async onLoad() {
    AudioManager.instance.registerBGM(BGMType.Level, this.levle_audio);
    AudioManager.instance.registerSFX(SFXType.Remove, this.remove_audio);
    AudioManager.instance.registerSFX(SFXType.Victory, this.victory_sfx_audio);
    AudioManager.instance.registerSFX(SFXType.Magic, this.magic_sfx_audio);
    AudioManager.instance.registerSFX(SFXType.Hint, this.hint_sfx_audio);
    AudioManager.instance.registerSFX(SFXType.Shuffle, this.shuffle_sfx_audio);

    AudioManager.instance.setCurBgm(BGMType.Level);
    AudioManager.instance.playBGM(BGMType.Level);

    this.toolBadge = this.Function_Node.getComponent(ButtonBadge);

    const frames = await loadSpriteFrames("textures");
    this.spriteFrames = frames;

    this.currentShapeNode = new Node("BoardRoot");
    this.currentShapeNode.addComponent(UITransform);
    this.node.addChild(this.currentShapeNode);

    // 开始关卡
    this.startLevel(LevelManager.instance.getCurrentLevel());

    this.bindGlobalTouch();
  }

  private startLevel(config) {
    this.isTutorial = !!config.isTutorial;
    this.tutorialStep = 1;
    this.currentShapeNode.removeAllChildren();
    if (this.handNode && this.handNode !== this.hintManager?.handNode) {
      this.handNode.destroy();
    }
    this.handNode = null;

    this.levelBuilder = new LevelBuilder(
      this.currentShapeNode,
      this.blockPrefab,
      this.spriteFrames,
      config,
    );
    this.levelBuilder.buildBoard();

    // 赋值给 GameManager
    this.board = this.levelBuilder.board;
    this.blockNodes = this.levelBuilder.blockNodes;
    this.rows = this.levelBuilder.rows;
    this.cols = this.levelBuilder.cols;
    this.blockSize = config.blockSize;
    this.playEnterAnimation();
    this.dragManager = new DragManager(
      this.board,
      this.blockNodes,
      this.rows,
      this.cols,
      this.blockSize,
    );
    console.log("关卡生成完成", this.board, this.blockNodes);

    if (this.isTutorial) {
      this.initTutorial();
    }
  }

  private initTutorial() {
    // 尝试获取 HintManager
    if (!this.hintManager) {
      this.hintManager = this.getComponent(HintManager);
    }

    // 尝试获取 Hand 节点
    if (this.hintManager && this.hintManager.handNode) {
      this.handNode = this.hintManager.handNode;
    } else {
      // 尝试在场景中查找 Hand
      const foundHand =
        this.node.getChildByName("Hand") ||
        this.node.scene.getChildByName("Hand");
      if (foundHand) {
        this.handNode = foundHand;
      } else {
        console.warn(
          "HintManager.handNode is not set, trying to load dynamically...",
        );
        // 动态加载手势资源作为后备方案
        resources.load(
          "ui/hand/spriteFrame",
          SpriteFrame,
          (err, spriteFrame) => {
            if (err) {
              console.error("动态加载手势失败", err);
              return;
            }
            const node = new Node("Hand");
            const sprite = node.addComponent(Sprite);
            sprite.spriteFrame = spriteFrame;
            this.node.addChild(node);
            this.handNode = node;
            node.layer = this.node.layer;
            this.updateTutorialStep();
          },
        );
        return; // 等待加载完成
      }
    }

    if (this.handNode) {
      this.updateTutorialStep();
    } else {
      console.error("无法找到手势节点 (Hand Node)，教学引导可能无法显示");
    }
  }

  private updateTutorialStep() {
    if (!this.handNode) return;
    this.handNode.active = true;
    this.handNode.setSiblingIndex(999); // Ensure on top
    Tween.stopAllByTarget(this.handNode); // Clean existing tweens

    // Fixed board:
    // [0, 0, -1]
    // [1, -1, 1]
    // Step 1: Click (0,0)
    // Step 2: Click (0,1)
    // Step 3: Drag (1,0) -> (1,1)

    let targetPos: Vec3;
    let targetPos2: Vec3;

    switch (this.tutorialStep) {
      case 1: // Point at (0,0)
        targetPos = this.getPos(0, 0);
        this.handNode.setPosition(targetPos.x + 30, targetPos.y - 30, 0);
        tween(this.handNode)
          .repeatForever(
            tween()
              .to(0.5, { scale: new Vec3(1.2, 1.2, 1) })
              .to(0.5, { scale: new Vec3(1, 1, 1) }),
          )
          .start();
        break;
      case 2: // Point at (0,1)
        targetPos = this.getPos(0, 1);
        this.handNode.setPosition(targetPos.x + 30, targetPos.y - 30, 0);
        tween(this.handNode)
          .repeatForever(
            tween()
              .to(0.5, { scale: new Vec3(1.2, 1.2, 1) })
              .to(0.5, { scale: new Vec3(1, 1, 1) }),
          )
          .start();
        break;
      case 3: // Drag (1,0) -> (1,1)
        targetPos = this.getPos(1, 0);
        targetPos2 = this.getPos(1, 1);
        this.handNode.setPosition(targetPos);
        tween(this.handNode)
          .repeatForever(
            tween()
              .to(1.0, { position: targetPos2 })
              .delay(0.2)
              .set({ position: targetPos }),
          )
          .start();
        break;
      default:
        this.handNode.active = false;
        break;
    }
  }

  /**
   * resetState
   */
  public resetState() {
    this.firstClicked = null;
    this.markedBlocks.clear();
  }

  /** 播放进入关卡时的淡入动画 */
  private playEnterAnimation() {
    for (let r = 0; r < this.levelBuilder.blockNodes.length; r++) {
      for (let c = 0; c < this.levelBuilder.blockNodes[r].length; c++) {
        const node = this.levelBuilder.blockNodes[r][c];
        if (node && node.isValid) {
          node.setScale(0, 0, 0);
          tween(node)
            .to(0.3, { scale: new Vec3(1, 1, 1) })
            .start();
        }
      }
    }
  }

  public async onLevelComplete() {
    this.resetState();
    const oldNodes: Node[] = [];
    for (let r = 0; r < this.levelBuilder.blockNodes.length; r++) {
      for (let c = 0; c < this.levelBuilder.blockNodes[r].length; c++) {
        const node = this.levelBuilder.blockNodes[r][c];
        if (node && node.isValid) oldNodes.push(node);
      }
    }
    console.log(3333, oldNodes);
    // 等待旧棋盘缩小消失
    await Promise.all(
      oldNodes.map(
        (n) =>
          new Promise<void>((resolve) => {
            tween(n)
              .to(0.3, { scale: new Vec3(0, 0, 0) })
              .call(() => {
                n.destroy();
                resolve();
              })
              .start();
          }),
      ),
    );
    // 进入下一关
    const next = LevelManager.instance.nextLevel();
    if (next) {
      console.log("恭喜，下一关");
      AudioManager.instance.playSFX(SFXType.Victory);
      this.startLevel(next);
    } else {
      console.log("恭喜，全部关卡通关！");
      // 这里可以加个结算UI 或者重新开始
      LevelManager.instance.reset();
      this.startLevel(LevelManager.instance.getCurrentLevel());
    }
  }

  start() {}

  getPos(row: number, col: number): Vec3 {
    const x =
      col * this.blockSize -
      (this.cols * this.blockSize) / 2 +
      this.blockSize / 2;
    const y =
      (this.rows * this.blockSize) / 2 -
      row * this.blockSize -
      this.blockSize / 2;
    return new Vec3(x, y, 0);
  }

  setBlockBgColor(block: Node, color: Color) {
    if (block) {
      const bgNode = block.getChildByName("Bg");
      const bgSprite = bgNode.getComponent(Sprite)!;
      bgSprite.color = color;
    }
  }

  bindGlobalTouch() {
    input.on(Input.EventType.TOUCH_START, (event: EventTouch) =>
      this.handleTouchStart(event),
    );

    input.on(Input.EventType.TOUCH_MOVE, (event: EventTouch) =>
      this.handleTouchMove(event),
    );

    input.on(Input.EventType.TOUCH_END, () => this.handleTouchEnd());

    input.on(Input.EventType.TOUCH_CANCEL, (event: EventTouch) =>
      this.handleTouchCancel(),
    );
  }

  // 开始滑动
  handleTouchStart(event) {
    this.hintManager.cancelHint();
    this.resetHintPair();
    this.clearMarkVisuals();
    const pos = event.getUILocation();
    const res = this.findBlockAtPosition(pos);
    if (!res) return;

    const { block, r, c } = res;

    if (this.isTutorial) {
      if (this.tutorialStep === 1 && (r !== 0 || c !== 0)) return;
      if (this.tutorialStep === 2 && (r !== 0 || c !== 1)) return;
      if (this.tutorialStep === 3 && (r !== 1 || c !== 0)) return;
      if (this.tutorialStep > 3) return;
    }

    this.draggingBlock = block;
    this.dragRow = r;
    this.dragCol = c;
    this.dragStartPos = pos.clone();
    this.dragStartBlockPos = block.getPosition().clone();
    this.slidingDir = null;
    this.draggingGroup = []; // 初始化空数组

    this.setBlockBgColor(this.draggingBlock, new Color(255, 100, 100));
  }

  // 拖动中
  handleTouchMove(event) {
    if (!this.draggingBlock) return;

    if (this.isTutorial && this.tutorialStep < 3) return;

    const pos = event.getUILocation();
    const dx = pos.x - this.dragStartPos.x;
    const dy = pos.y - this.dragStartPos.y;

    // 没锁定滑动方向前
    if (!this.slidingDir) {
      if (Math.abs(dx) < this.threshold && Math.abs(dy) < this.threshold)
        return;

      // 根据滑动方向锁定行或列滑动
      if (Math.abs(dx) > Math.abs(dy)) {
        this.slidingDir = { dirR: 0, dirC: dx > 0 ? 1 : -1 };
      } else {
        this.slidingDir = { dirR: dy > 0 ? -1 : 1, dirC: 0 };
      }
    }

    this.draggingGroup = this.dragManager.findDragGroup(
      this.dragRow,
      this.dragCol,
      this.slidingDir.dirR,
      this.slidingDir.dirC,
    );
    // 检查整组前方空格是否足够滑动
    const maxSteps = this.dragManager.countEmptyStepsGroup(
      this.draggingGroup,
      this.slidingDir.dirR,
      this.slidingDir.dirC,
    );
    if (maxSteps === 0) {
      this.slidingDir = null;
      this.draggingGroup = [];
      return;
    }

    this.dragManager.applyDragOffset(
      this.draggingGroup,
      maxSteps,
      this.slidingDir.dirR,
      this.slidingDir.dirC,
      dy,
      dx,
    );
  }

  // 滑动结束
  async handleTouchEnd() {
    if (!this.draggingBlock) return;

    if (!this.slidingDir) {
      // 没滑动，单击消除逻辑复用
      this.onBlockClick(this.dragRow, this.dragCol);

      // 复位单个方块位置
      this.draggingBlock.setPosition(this.getPos(this.dragRow, this.dragCol));
    } else {
      // 计算滑动步数
      let steps = 0;
      const firstBlockPos = this.getPos(this.dragRow, this.dragCol);
      const curPos = this.draggingBlock.getPosition();

      if (this.slidingDir.dirR !== 0) {
        const delta = curPos.y - firstBlockPos.y;
        steps = Math.round(delta / this.blockSize) * this.slidingDir.dirR * -1;
      } else {
        const delta = curPos.x - firstBlockPos.x;
        steps = Math.round(delta / this.blockSize) * this.slidingDir.dirC;
      }

      const maxSteps = this.dragManager.countEmptyStepsGroup(
        this.draggingGroup,
        this.slidingDir.dirR,
        this.slidingDir.dirC,
      );
      if (steps > maxSteps) steps = maxSteps;
      if (steps < 0) steps = 0;

      if (steps === 0) {
        // 无滑动，复位所有方块位置
        this.draggingGroup.forEach(({ r, c }) => {
          const block = this.blockNodes[r][c];
          if (block) block.setPosition(this.getPos(r, c));
        });
      } else {
        // 有滑动的情况

        const type = this.board[this.dragRow][this.dragCol];
        // 先缓存所有方块类型，拖动多块时保持准确
        const blocksInfo = this.draggingGroup.map(({ r, c }) => ({
          r,
          c,
          type: this.board[r][c],
          node: this.blockNodes[r][c],
        }));
        // 先把原位置置空
        blocksInfo.forEach(({ r, c }) => {
          this.board[r][c] = -1;
          this.blockNodes[r][c] = null;
        });

        // 按拖动方向排序，避免位置覆盖冲突
        const sortedBlocks = [...blocksInfo].sort((a, b) => {
          if (this.slidingDir.dirR !== 0) {
            return this.slidingDir.dirR === 1 ? b.r - a.r : a.r - b.r;
          } else {
            return this.slidingDir.dirC === 1 ? b.c - a.c : a.c - b.c;
          }
        });

        // 清理状态
        this.firstClicked = null;
        this.markedBlocks.clear();

        // 只针对发起拖动的方块（起点）
        const startTr = this.dragRow + this.slidingDir.dirR * steps;
        const startTc = this.dragCol + this.slidingDir.dirC * steps;
        const startType = type;

        const neighbors = this.findAdjacentSameType(
          startTr,
          startTc,
          startType,
          blocksInfo,
          this.slidingDir,
        );

        if (neighbors.length === 0) {
          this.dragManager.settleDrag(blocksInfo, true);
          this.setBlockBgColor(this.draggingBlock, new Color(255, 255, 255));
        } else {
          // 移动到新位置，更新board和blockNodes
          this.dragManager.settleDrag(
            sortedBlocks,
            false,
            steps,
            this.slidingDir,
          );
          if (neighbors.length === 1) {
            this.playRemoveAudio();
            // 唯一邻居，连线并消除起点和邻居
            const onlyNeighbor = neighbors[0];
            this.setBlockBgColor(
              this.blockNodes[onlyNeighbor.nr][onlyNeighbor.nc],
              new Color(255, 100, 100),
            );

            this.drawLinkLine(
              this.blockNodes[startTr][startTc],
              this.blockNodes[onlyNeighbor.nr][onlyNeighbor.nc],
            );
            this.scheduleOnce(() => {
              this.removeBlock(startTr, startTc);
              this.removeBlock(onlyNeighbor.nr, onlyNeighbor.nc);
              const empty = this.isBoardEmpty();
              if (empty) {
                this.onLevelComplete();
                console.log("下一关");
              } else {
                const result = this.hasAvailableMove();
                console.log(`${result ? "还有可移动的了" : "没有可移动的了"}`);
              }
            }, 0.5);
          } else {
            // 多邻居，标记高亮起点和邻居
            this.markedBlocks.clear();
            // this.markedBlocks.add(`${startTr},${startTc}`);
            this.setBlockBgColor(
              this.blockNodes[startTr][startTc],
              new Color(255, 100, 100),
            );

            neighbors.forEach(({ nr, nc }) =>
              this.markedBlocks.add(`${nr},${nc}`),
            );
            this.updateMarkVisuals();
            this.firstClicked = { r: startTr, c: startTc };
          }
        }
      }
    }
    this.setBlockBgColor(this.draggingBlock, new Color(255, 255, 255));

    this.draggingBlock = null;
    this.draggingGroup = [];
    this.slidingDir = null;
  }

  // 取消滑动
  handleTouchCancel() {
    if (!this.draggingBlock) return;
    // 复位所有拖动方块位置
    this.setBlockBgColor(this.draggingBlock, new Color(255, 255, 255));
    if (this.draggingGroup.length > 0) {
      this.draggingGroup.forEach(({ r, c }) => {
        const block = this.blockNodes[r][c];
        if (block) block.setPosition(this.getPos(r, c));
      });
    } else {
      this.draggingBlock.setPosition(this.getPos(this.dragRow, this.dragCol));
    }
    this.draggingBlock = null;
    this.draggingGroup = [];
    this.slidingDir = null;
  }

  findBlockAtPosition(pos: Vec2): { block: Node; r: number; c: number } | null {
    const uiTransform =
      this.currentShapeNode.getComponent(UITransform) ||
      this.currentShapeNode.addComponent(UITransform);
    const localPos = uiTransform.convertToNodeSpaceAR(
      new Vec3(pos.x, pos.y, 0),
    );

    const c = Math.floor(
      (localPos.x + (this.cols * this.blockSize) / 2) / this.blockSize,
    );
    const r = Math.floor(
      ((this.rows * this.blockSize) / 2 - localPos.y) / this.blockSize,
    );

    if (r >= 0 && r < this.rows && c >= 0 && c < this.cols) {
      const block = this.blockNodes[r][c];
      if (block) {
        return { block, r, c };
      }
    }
    return null;
  }

  countEmptySteps(r: number, c: number, dirR: number, dirC: number): number {
    let count = 0;
    let nr = r + dirR;
    let nc = c + dirC;
    while (nr >= 0 && nr < this.rows && nc >= 0 && nc < this.cols) {
      if (this.board[nr][nc] === -1) count++;
      else break;
      nr += dirR;
      nc += dirC;
    }
    return count;
  }

  // 点击方块处理 zzc
  onBlockClick(r: number, c: number) {
    if (this.isTutorial) {
      if (this.tutorialStep === 1 && r === 0 && c === 0) {
        this.tutorialStep = 2;
        this.updateTutorialStep();
      } else if (this.tutorialStep === 2 && r === 0 && c === 1) {
        this.tutorialStep = 3;
        this.updateTutorialStep();
      }
    }

    const key = `${r},${c}`;
    const type = this.board[r][c];
    if (type < 0) return; // 空格不处理
    if (!this.firstClicked) {
      // 第一次点击
      this.firstClicked = { r, c };
      this.markedBlocks.clear();

      // 四个方向找邻居
      const dirs = [
        { dr: -1, dc: 0 }, // 上
        { dr: 1, dc: 0 }, // 下
        { dr: 0, dc: -1 }, // 左
        { dr: 0, dc: 1 }, // 右
      ];

      let validNeighbors: { nr: number; nc: number }[] = [];

      dirs.forEach(({ dr, dc }) => {
        const neighbor = this.findFirstNeighbor(r, c, dr, dc);
        if (neighbor && this.board[neighbor.nr][neighbor.nc] === type) {
          validNeighbors.push(neighbor);
          this.markedBlocks.add(`${neighbor.nr},${neighbor.nc}`);
        }
      });
      if (validNeighbors.length === 1) {
        // 唯一邻居 → 直接消除
        const onlyNeighbor = validNeighbors[0];
        this.setBlockBgColor(
          this.blockNodes[onlyNeighbor.nr][onlyNeighbor.nc],
          new Color(255, 100, 100),
        );
        this.drawLinkLine(
          this.blockNodes[r][c],
          this.blockNodes[onlyNeighbor.nr][onlyNeighbor.nc],
        );

        this.playRemoveAudio();

        this.scheduleOnce(() => {
          this.removeBlock(r, c);
          this.removeBlock(onlyNeighbor.nr, onlyNeighbor.nc);

          this.firstClicked = null;
          this.markedBlocks.clear();
          const empty = this.isBoardEmpty();
          if (empty) {
            this.onLevelComplete();
            console.log("下一关");
          } else {
            const result = this.hasAvailableMove();
            console.log(`${result ? "还有可移动的了" : "没有可移动的了"}`);
          }
        }, 0.5);

        return;
      }

      // 否则高亮标记
      this.updateMarkVisuals();
      return;
    }

    // 第二次点击
    if (this.markedBlocks.has(key)) {
      this.playRemoveAudio();
      this.drawLinkLine(
        this.blockNodes[this.firstClicked.r][this.firstClicked.c],
        this.blockNodes[r][c],
      );
      const targets = [this.firstClicked, { r, c }];
      this.scheduleOnce(() => {
        targets.forEach(({ r, c }) => this.removeBlock(r, c));
      }, 0.5);
      this.clearMarkVisuals();

      this.markedBlocks.clear();
      this.firstClicked = null;
      const empty = this.isBoardEmpty();
      if (empty) {
        this.onLevelComplete();
        console.log("下一关");
      } else {
        const result = this.hasAvailableMove();
        console.log(`${result ? "还有可移动的了" : "没有可移动的了"}`);
      }
    } else {
      // 重新选择
      this.clearMarkVisuals();
      this.markedBlocks.clear();
      this.firstClicked = null;
      this.onBlockClick(r, c);
    }
  }

  findFirstNeighbor(r: number, c: number, dr: number, dc: number) {
    let nr = r + dr;
    let nc = c + dc;
    while (nr >= 0 && nr < this.rows && nc >= 0 && nc < this.cols) {
      if (this.board[nr][nc] >= 0) {
        return { nr, nc }; // 找到第一个非空
      }
      nr += dr;
      nc += dc;
    }
    return null; // 这个方向没有邻居
  }

  removeBlock(r: number, c: number) {
    this.board[r][c] = -1;
    if (this.blockNodes[r][c]) {
      this.blockNodes[r][c].destroy();
      this.blockNodes[r][c] = null;
    }
  }

  // 找当前方块上下左右相邻且同类型的方块（单格，不连线）
  findAdjacentSameType(
    r: number,
    c: number,
    type: number,
    dragGroup: { r: number; c: number }[] = [],
    slidingDir: { dirR: number; dirC: number } | null = null,
  ): { nr: number; nc: number }[] {
    const dirs = [
      { dr: -1, dc: 0 }, // 上
      { dr: 1, dc: 0 }, // 下
      { dr: 0, dc: -1 }, // 左
      { dr: 0, dc: 1 }, // 右
    ];

    const neighbors: { nr: number; nc: number }[] = [];

    dirs.forEach(({ dr, dc }) => {
      const neighbor = this.findFirstNeighbor(r, c, dr, dc);
      if (neighbor && this.board[neighbor.nr][neighbor.nc] === type) {
        if (
          dr === slidingDir.dirR &&
          dc === slidingDir.dirC &&
          dragGroup.length > 1
        ) {
        } else {
          neighbors.push(neighbor);
          this.markedBlocks.add(`${neighbor.nr},${neighbor.nc}`);
        }
      }
    });

    return neighbors;
  }

  updateMarkVisuals() {
    this.markedBlocks.forEach((key) => {
      const [r, c] = key.split(",").map(Number);
      this.setBlockBgColor(this.blockNodes[r][c], new Color(255, 255, 0));
    });
  }

  hasSameNeighbor(row: number, col: number, type: number): boolean {
    const dirs = [
      { dr: -1, dc: 0 }, // 上
      { dr: 1, dc: 0 }, // 下
      { dr: 0, dc: -1 }, // 左
      { dr: 0, dc: 1 }, // 右
    ];
    for (const { dr, dc } of dirs) {
      const nr = row + dr;
      const nc = col + dc;
      if (nr >= 0 && nr < this.rows && nc >= 0 && nc < this.cols) {
        if (this.board[nr][nc] === type) {
          return true;
        }
      }
    }
    return false;
  }

  clearMarkVisuals() {
    this.markedBlocks.forEach((key) => {
      const [r, c] = key.split(",").map(Number);
      this.setBlockBgColor(this.blockNodes[r][c], new Color(255, 255, 255));
    });
  }
  // 消除所有标记的方块，并清理状态
  clearMarkedBlocks() {
    if (this.markedBlocks.size === 0) return;

    for (const key of this.markedBlocks) {
      const [r, c] = key.split(",").map(Number);
      this.board[r][c] = -1;
      const block = this.blockNodes[r][c];
      if (block) {
        block.active = false;
        this.blockNodes[r][c] = null;
      }
    }

    this.markedBlocks.clear();
    // this.selectedBlock = null;
    this.clearMarkVisuals();
  }

  drawLinkLine(blockA: Node, blockB: Node) {
    const g = this.lineLayer.getComponent(Graphics);
    g.clear();

    // 获取世界坐标
    const posAWorld = blockA.worldPosition;
    const posBWorld = blockB.worldPosition;
    // 转换到 lineLayer 本地坐标
    const posA = this.lineLayer
      .getComponent(UITransform)
      .convertToNodeSpaceAR(posAWorld);
    const posB = this.lineLayer
      .getComponent(UITransform)
      .convertToNodeSpaceAR(posBWorld);

    // 绘制线
    g.lineWidth = 5;
    g.strokeColor = Color.YELLOW;
    g.moveTo(posA.x, posA.y);
    g.lineTo(posB.x, posB.y);
    g.stroke();

    // 1 秒后清除
    this.scheduleOnce(() => {
      g.clear();
    }, 0.5);
  }

  isBoardEmpty(): boolean {
    for (let r = 0; r < this.rows; r++) {
      for (let c = 0; c < this.cols; c++) {
        if (this.board[r][c] >= 0) return false;
      }
    }
    return true;
  }

  hasAvailableMove(): boolean {
    return this.findFirstEliminablePair() !== null;
  }

  // 辅助函数：模拟 group 移动 step 步后，是否有可消除邻居
  getNeighborsAfterMove(
    r: number,
    c: number,
    step: number,
    dragGroup: { r: number; c: number }[] = [],
    slidingDir: { dr: number; dc: number } | null = null,
  ): { nr: number; nc: number }[] {
    const type = this.board[r][c];

    // 预计算移动后的位置和原位置
    const newPositions = new Set<string>();
    const oldPositions = new Set<string>();

    dragGroup.forEach(({ r: gr, c: gc }) => {
      const tr = gr + slidingDir.dr * step;
      const tc = gc + slidingDir.dc * step;
      newPositions.add(`${tr},${tc}`);
      oldPositions.add(`${gr},${gc}`);
    });

    const dirs = [
      { dr: -1, dc: 0 }, // 上
      { dr: 1, dc: 0 }, // 下
      { dr: 0, dc: -1 }, // 左
      { dr: 0, dc: 1 }, // 右
    ];

    const neighbors: { nr: number; nc: number }[] = [];

    // 目标位置（也就是当前检查的方块移动后的位置）
    const tr = r + slidingDir.dr * step;
    const tc = c + slidingDir.dc * step;

    dirs.forEach(({ dr, dc }) => {
      let nr = tr + dr;
      let nc = tc + dc;

      while (nr >= 0 && nr < this.rows && nc >= 0 && nc < this.cols) {
        const key = `${nr},${nc}`;
        let cellType = -1;

        if (newPositions.has(key)) {
          // 是移动过来的方块，找到它的原始位置获取类型
          const or = nr - slidingDir.dr * step;
          const oc = nc - slidingDir.dc * step;
          cellType = this.board[or][oc];
        } else if (oldPositions.has(key)) {
          // 是原来的位置，但现在空了（且没被新方块占据）
          cellType = -1;
        } else {
          // 静态方块
          cellType = this.board[nr][nc];
        }

        if (cellType >= 0) {
          // 找到第一个非空
          if (cellType === type) {
            // 排除自身移动方向上的内部连接（通常不需要消除）
            if (
              dr === slidingDir.dr &&
              dc === slidingDir.dc &&
              dragGroup.length > 1
            ) {
              // ignore
            } else {
              neighbors.push({ nr, nc });
            }
          }
          break;
        } else if (cellType === -2) {
          // 石头，阻挡
          break;
        }

        nr += dr;
        nc += dc;
      }
    });

    return neighbors;
  }

  /** 四方向常量 */
  private static readonly DIRS = [
    { dr: -1, dc: 0 }, // 上
    { dr: 1, dc: 0 }, // 下
    { dr: 0, dc: -1 }, // 左
    { dr: 0, dc: 1 }, // 右
  ];

  /**
   * 核心：找到第一对“可以消除”的方块
   * 规则覆盖：
   *  - 直接相邻；
   *  - 单格滑动（穿过空格）；
   *  - 多块组（连续段）从两端向外滑动（两端之外第一非空如果同色就可消）
   * 返回 null 表示不存在
   */
  private findFirstEliminablePair(): {
    from: { r: number; c: number };
    to: { r: number; c: number };
  } | null {
    for (let r = 0; r < this.rows; r++) {
      for (let c = 0; c < this.cols; c++) {
        const type = this.board[r][c];
        if (type < 0) continue;

        // 1️⃣ 单格邻居提示
        const hint = this.checkSingleNeighbor(r, c);
        if (hint) return hint;

        // 2️⃣ 滑动提示
        const hint2 = this.checkMoveNeighbor(r, c);
        if (hint2) return hint2;
      }
    }

    return null;
  }

  /**
   * 提示函数
   * 返回可操作消除的一组方块坐标 [{r, c}, ...]
   * 如果没有可消除组，返回空数组 []
   */
  getHint(): {
    from: { r: number; c: number };
    to: { r: number; c: number };
    steps?: number;
    dir?: { dr: number; dc: number };
  } | null {
    return this.findFirstEliminablePair();
  }

  // 单格邻居检测
  checkSingleNeighbor(r: number, c: number) {
    const type = this.board[r][c];
    for (const { dr, dc } of GameManager.DIRS) {
      const nr = r + dr;
      const nc = c + dc;
      if (nr < 0 || nr >= this.rows || nc < 0 || nc >= this.cols) continue;
      if (this.board[nr][nc] === type) {
        return { from: { r, c }, to: { r: nr, c: nc } };
      }
    }
    return null;
  }

  // 滑动提示
  checkMoveNeighbor(r: number, c: number) {
    for (const { dr, dc } of GameManager.DIRS) {
      const group = this.dragManager.findDragGroup(r, c, dr, dc);
      const maxSteps = this.dragManager.countEmptyStepsGroup(group, dr, dc);
      if (maxSteps === 0) continue;

      for (let step = 1; step <= maxSteps; step++) {
        const neighbors = this.getNeighborsAfterMove(r, c, step, group, {
          dr,
          dc,
        });
        if (neighbors.length > 0) {
          const { nr, nc } = neighbors[0];
          return {
            from: { r, c },
            to: { r: nr, c: nc },
            steps: step,
            dir: { dr, dc },
          };
        }
      }
    }
    return null;
  }

  highlightHint() {
    const count = this.toolBadge.getHintCount();
    if (count <= 0) {
      console.log("看广告");
      this.toolBadge.showAdAndReward("hint");
      return;
    }
    const hint = this.getHint();
    if (hint) {
      AudioManager.instance.playSFX(SFXType.Hint);
      if (hint?.steps) {
        this.hintManager.showHint(hint, this.getPos.bind(this));
      }
      this.toolBadge.useHintOnce();

      this.hintPair.from = hint.from;
      this.hintPair.to = hint.to;

      this.setBlockBgColor(
        this.blockNodes[hint.from.r][hint.from.c],
        new Color(255, 100, 100),
      );

      this.setBlockBgColor(
        this.blockNodes[hint.to.r][hint.to.c],
        new Color(255, 100, 100),
      );
    } else {
      console.log("没有可消除的组合");
    }
    // if (hintBlocks.length === 0) return;

    // hintBlocks.forEach(({ nr, nc }) => {
    //   const node = this.blockNodes[nr][nc];
    //   if (!node) return;

    //   // 闪烁动画：颜色在原色和高亮色之间切换
    //   const originalColor = node.getComponent(Sprite)!.color.clone();
    //   const highlightColor = new Color(255, 255, 0); // 黄色高亮

    //   tween(node.getComponent(Sprite)!)
    //     .to(0.3, { color: highlightColor })
    //     .to(0.3, { color: originalColor })
    //     .to(0.3, { color: highlightColor })
    //     .to(0.3, { color: originalColor })
    //     .start();
    // });
  }

  playRemoveAudio() {
    AudioManager.instance.playSFX(SFXType.Remove);
  }

  shuffleBoard(): void {
    const count = this.toolBadge.getShuffleCount();
    console.log("打乱次数", count);
    if (count <= 0) {
      console.log("看广告");
      this.toolBadge.showAdAndReward("shuffle");
      return;
    }

    // 1. 收集剩余方块类型
    const blocks: number[] = [];
    for (let r = 0; r < this.rows; r++) {
      for (let c = 0; c < this.cols; c++) {
        if (this.board[r][c] >= 0) {
          blocks.push(this.board[r][c]);
        }
      }
    }

    // 2. Fisher–Yates 随机打乱
    for (let i = blocks.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [blocks[i], blocks[j]] = [blocks[j], blocks[i]];
    }

    // 3. 重新写入 this.board
    let idx = 0;
    for (let r = 0; r < this.rows; r++) {
      for (let c = 0; c < this.cols; c++) {
        if (this.board[r][c] >= 0) {
          this.board[r][c] = blocks[idx++];
        }
      }
    }
    AudioManager.instance.playSFX(SFXType.Shuffle);
    // 4. 更新渲染状态 blockNodes
    this.updateBlockNodes();

    // 5. 确保有解，没有就重新 shuffle
    this.toolBadge.useShuffleOnce();

    if (!this.getHint()) {
      this.shuffleBoard(); // 递归重新打乱直到有解
    }
  }

  /**
   * 遍历 board 更新所有 blockNodes
   */
  updateBlockNodes(): void {
    for (let r = 0; r < this.rows; r++) {
      for (let c = 0; c < this.cols; c++) {
        const type = this.board[r][c];

        if (type >= 0) {
          let node = this.blockNodes[r][c];
          if (!node) {
            // 如果之前是空格，新建节点
            // node = this.createBlockNode(type, r, c);
            // this.blockNodes[r][c] = node;
            // this.board[r][c] = type;
            const block = this.levelBuilder.createBlockNode(type, r, c);
            this.blockNodes[r][c] = block;
          } else {
            // 更新已有节点的类型显示
            this.levelBuilder.setBlockType(node, type);
            this.levelBuilder.setBlockPosition(node, r, c);
          }
        } else if (type === -1) {
          // 空格 → 清理节点
          if (this.blockNodes[r][c]) {
            this.levelBuilder.destroyBlockNode(this.blockNodes[r][c]);
            this.blockNodes[r][c] = null;
          }
        } else if (type === -2) {
          // 石头 不处理
        }
      }
    }
  }

  // Fisher–Yates 洗牌
  shuffleArray<T>(arr: T[]): T[] {
    for (let i = arr.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    return arr;
  }

  // 检查棋盘是否有解
  hasSolution(board: number[][]): boolean {
    // 临时替换 this.board
    const backup = this.board;
    this.board = board;

    const hint = this.findFirstEliminablePair();

    this.board = backup; // 恢复
    return !!hint;
  }

  public eliminateRandomType(): void {
    const count = this.toolBadge.getMagicCount();
    if (count <= 0) {
      console.log("看广告");
      this.toolBadge.showAdAndReward("magic");
      return;
    }

    const types = new Set<number>();

    // 收集当前所有存在的方块类型
    for (let r = 0; r < this.rows; r++) {
      for (let c = 0; c < this.cols; c++) {
        const val = this.board[r][c];
        if (val >= 0) {
          types.add(val);
        }
      }
    }

    if (types.size === 0) {
      console.warn("没有可消除的方块了");
      this.onLevelComplete();
      return;
    }

    // 转成数组，随机挑一个
    const typeArray = Array.from(types);
    const randType = typeArray[Math.floor(Math.random() * typeArray.length)];

    console.log("随机消除类型:", randType);

    this.eliminateType(randType);
  }

  public eliminateType(type: number): void {
    this.toolBadge.useMagicOnce();
    AudioManager.instance.playSFX(SFXType.Magic);
    for (let r = 0; r < this.rows; r++) {
      for (let c = 0; c < this.cols; c++) {
        if (this.board[r][c] === type) {
          this.board[r][c] = -1;

          const node = this.blockNodes[r][c];
          if (node) {
            this.playEliminateAnimation(node, () => {
              this.destroyBlockNode(node);
              this.blockNodes[r][c] = null;
            });
          }
        }
      }
    }
    const empty = this.isBoardEmpty();

    if (empty) {
      setTimeout(() => {
        this.onLevelComplete();
        console.log("下一关");
      }, 1000);
    }
  }

  private playEliminateAnimation(node: Node, onFinish: () => void) {
    // 确保有透明组件
    let opacityComp = node.getComponent(UIOpacity);
    if (!opacityComp) {
      opacityComp = node.addComponent(UIOpacity);
    }
    opacityComp.opacity = 255;

    // 动画：缩放 → 轻微旋转 → 弹缩 → 渐隐消失
    tween(node)
      .parallel(
        tween().to(0.15, { scale: new Vec3(1.2, 1.2, 1) }), // 先放大一点
        tween().to(0.15, { angle: 30 }), // 顺时针轻微旋转
      )
      .parallel(
        tween().to(
          0.25,
          { scale: new Vec3(0.8, 0.8, 1) },
          { easing: "quadOut" },
        ), // 缩小回弹
        tween().to(0.25, { angle: -30 }), // 逆时针旋转
      )
      .parallel(
        tween().to(0.2, { scale: new Vec3(0, 0, 0) }), // 最终缩小
        tween(opacityComp).to(0.2, { opacity: 0 }), // 渐隐
      )
      .call(() => {
        onFinish();
      })
      .start();
  }

  public destroyBlockNode(node: Node): void {
    if (node && node.isValid) {
      node.removeFromParent();
      node.destroy();
    }
  }

  public resetHintPair() {
    if (
      this.isNumber(this.hintPair?.from?.r) &&
      this.isNumber(this.hintPair?.from?.c)
    ) {
      this.setBlockBgColor(
        this.blockNodes[this.hintPair?.from?.r][this.hintPair?.from?.c],
        new Color(255, 255, 255),
      );
    }

    if (
      this.isNumber(this.hintPair?.to?.r) &&
      this.isNumber(this.hintPair?.to?.c)
    ) {
      this.setBlockBgColor(
        this.blockNodes[this.hintPair.to.r][this.hintPair.to.c],
        new Color(255, 255, 255),
      );
    }

    this.hintPair = { from: {}, to: {} };
  }

  public isNumber(value) {
    return (
      value !== null &&
      value !== undefined &&
      typeof value === "number" &&
      !isNaN(value)
    );
  }
}
