import { Node, Vec3, tween } from "cc";

export interface DragCell {
  r: number;
  c: number;
  type: number;
  node: Node;
}

export interface DragGroup {
  cells: DragCell[];
  dirR: number;
  dirC: number;
  maxSteps: number;
}

export class DragManager {
  private board: number[][];
  private blockNodes: (Node | null)[][];
  private rows: number;
  private cols: number;
  private blockSize: number;

  constructor(
    board: number[][],
    blockNodes: (Node | null)[][],
    rows: number,
    cols: number,
    blockSize: number,
  ) {
    this.board = board;
    this.blockNodes = blockNodes;
    this.rows = rows;
    this.cols = cols;
    this.blockSize = blockSize;
  }

  /** 查找拖动组（从点击格子 + 方向出发） */
  findDragGroup(
    r: number,
    c: number,
    dirR: number,
    dirC: number,
  ): { r: number; c: number }[] {
    if (this.board[r][c] < 0) return [];

    const group: { r: number; c: number }[] = [];

    // 包含起点
    group.push({ r, c });

    if (dirR === 0 && dirC === 0) {
      // 无方向，返回只有自己
      return group;
    }

    if (dirR !== 0) {
      // 垂直方向，向前方向查找
      // 前面方向就是 dirR 方向（正负决定上或下）

      let rr = r + dirR;
      while (rr >= 0 && rr < this.rows) {
        if (this.board[rr][c] >= 0) {
          if (dirR > 0) {
            // 向下，push后面
            group.push({ r: rr, c });
          } else {
            // 向上，unshift前面
            group.unshift({ r: rr, c });
          }
          rr += dirR;
        } else {
          break;
        }
      }
    } else if (dirC !== 0) {
      // 水平方向，向前方向查找
      // 前面方向就是 dirC 方向（正负决定左或右）

      let cc = c + dirC;
      while (cc >= 0 && cc < this.cols) {
        if (this.board[r][cc] >= 0) {
          if (dirC > 0) {
            // 向右，push后面
            group.push({ r, c: cc });
          } else {
            // 向左，unshift前面
            group.unshift({ r, c: cc });
          }
          cc += dirC;
        } else {
          break;
        }
      }
    }

    return group;
  }

  /** 计算最多能移动多少格 */
  countEmptyStepsGroup(
    group: { r: number; c: number }[],
    dirR: number,
    dirC: number,
  ): number {
    if (group.length === 0) return 0;
    // 按滑动方向，找整组前方连续空格最小值
    if (dirR !== 0) {
      // 纵向滑动，找组中最顶部或最底部行号
      const rows = group.map((g) => g.r);
      const col = group[0].c;
      const boundaryRow = dirR === 1 ? Math.max(...rows) : Math.min(...rows);
      let count = 0;
      let checkRow = boundaryRow + dirR;
      while (
        checkRow >= 0 &&
        checkRow < this.rows &&
        this.board[checkRow][col] === -1
      ) {
        count++;
        checkRow += dirR;
      }
      return count;
    } else {
      // 横向滑动，找组中最左或最右列号
      const cols = group.map((g) => g.c);
      const row = group[0].r;
      const boundaryCol = dirC === 1 ? Math.max(...cols) : Math.min(...cols);
      let count = 0;
      let checkCol = boundaryCol + dirC;
      while (
        checkCol >= 0 &&
        checkCol < this.cols &&
        this.board[row][checkCol] === -1
      ) {
        count++;
        checkCol += dirC;
      }
      return count;
    }
  }

  /** 执行拖动中的偏移 */
  public applyDragOffset(
    draggingGroup,
    maxSteps: number,
    dirR: number,
    dirC: number,
    dy: number,
    dx: number,
  ) {
    // 计算偏移量，滑动方向相反时置0
    let offset = dirR !== 0 ? dy : dx;

    if (
      (dirR === -1 && offset <= 0) ||
      (dirR === 1 && offset >= 0) ||
      (dirC === 1 && offset <= 0) ||
      (dirC === -1 && offset >= 0)
    )
      offset = 0;

    const maxOffset = maxSteps * this.blockSize;
    if (Math.abs(offset) > maxOffset) offset = maxOffset * Math.sign(offset);

    // 整组方块移动
    draggingGroup.forEach(({ r, c }) => {
      const block = this.blockNodes[r][c];
      if (!block) return;
      const startPos = this.getPos(r, c);
      if (dirR !== 0) {
        block.setPosition(startPos.x, startPos.y + offset, 0);
      } else {
        block.setPosition(startPos.x + offset, startPos.y, 0);
      }
    });
  }

  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);
  }

  /** 松手：确认移动 or 回弹 */
  public async settleDrag(
    blocksInfo: DragCell[],
    reset: boolean,
    steps?: number,
    slidingDir?,
  ) {
    if (reset) {
      await this.resetGroup(blocksInfo);
    } else {
      await this.applyMove(blocksInfo, steps, slidingDir);
    }
  }

  /** 执行动画移动 */
  private async applyMove(blocksInfo: DragCell[], steps: number, slidingDir) {
    blocksInfo.forEach(({ r, c, type, node }) => {
      const tr = r + slidingDir.dirR * steps;
      const tc = c + slidingDir.dirC * steps;

      this.board[tr][tc] = type;
      this.blockNodes[tr][tc] = node;
      if (node) node.setPosition(this.getPos(tr, tc));
    });
  }

  /** 回弹动画 */
  private async resetGroup(blocksInfo) {
    const promises = blocksInfo.map(({ r, c, type, node }) => {
      const targetPos = this.getPos(r, c);
      this.board[r][c] = type;
      this.blockNodes[r][c] = node;
      return new Promise<void>((resolve) => {
        tween(node)
          .to(0.15, { position: targetPos })
          .call(() => resolve()) // ✅ 修复
          .start();
      });
    });
    await Promise.all(promises);
  }
}
