import Bubble, { BUBBLE_RADIUS, gridToScreen, getActiveColorCount, drawBubble3D, EVEN_ROW_COLS, ODD_ROW_COLS } from './bubble';
import { SCREEN_WIDTH, SCREEN_HEIGHT, SAFE_AREA_TOP, SAFE_AREA_BOTTOM, IS_PAD } from '../render';
// 行高（六边形紧排）
const ROW_HEIGHT = BUBBLE_RADIUS * Math.sqrt(3);
// 消除所需的最少连通同色泡泡数
const MIN_POP_COUNT = 3;
// 触发补行的最低活跃行数（iPad 屏幕矮，行数过多会过快触顶）
const MIN_LIVE_ROWS = IS_PAD ? 7 : 10;

/**
 * 基于种子的伪随机数生成器（xorshift32）
 * 相同种子产生相同序列，保证同房间 ID 布局一致
 */
function seededRng(seed) {
  let s = (seed ^ 0xA5A5A5A5) >>> 0 || 1;
  return function () {
    s ^= s << 13;
    s ^= s >>> 17;
    s ^= s << 5;
    s = s >>> 0;
    return s / 0x100000000;
  };
}

/**
 * 形状模板：每个模板是一个数组，每项为该行需要放置泡泡的列索引数组。
 * 行从 0 开始，偶数行（0,2,4...）最多 11 列（0-10），奇数行（1,3,5...）最多 10 列（0-9）。
 * 网格坐标规则：偶数行 col C 的屏幕 x = C*2R+R，奇数行 col C 的屏幕 x = C*2R+2R。
 * 视觉对称中心：偶数行 col 5（x=11R），奇数行 col4/col5 之间（x=11R）。
 */
const SHAPE_TEMPLATES = [
  // 0: 倒三角 ▽ — 顶部宽，向下收窄至一点
  [
    [1,2,3,4,5,6,7,8,9],   // row 0 (even)
    [1,2,3,4,5,6,7,8],     // row 1 (odd)
    [2,3,4,5,6,7,8],       // row 2 (even)
    [2,3,4,5,6,7],         // row 3 (odd)
    [3,4,5,6,7],           // row 4 (even)
    [3,4,5,6],             // row 5 (odd)
    [4,5,6],               // row 6 (even)
    [4,5],                 // row 7 (odd)
    [5],                   // row 8 (even)
  ],
  // 1: 菱形 ◇ — 顶部一点，中间最宽，向下收回一点
  [
    [5],                   // row 0 (even)
    [4,5],                 // row 1 (odd)
    [4,5,6],               // row 2 (even)
    [3,4,5,6],             // row 3 (odd)
    [2,3,4,5,6,7,8],       // row 4 (even)
    [2,3,4,5,6,7],         // row 5 (odd)
    [3,4,5,6,7],           // row 6 (even)
    [3,4,5,6],             // row 7 (odd)
    [4,5,6],               // row 8 (even)
    [4,5],                 // row 9 (odd)
    [5],                   // row 10 (even)
  ],
  // 2: 心形 ♥ — 顶部双鼓包，向下收窄至一点
  [
    [2,3,4,6,7,8],         // row 0 (even) — 左右双鼓包，中间留空
    [1,2,3,4,5,6,7,8],     // row 1 (odd)  — 鼓包合并
    [1,2,3,4,5,6,7,8,9],   // row 2 (even)
    [1,2,3,4,5,6,7,8],     // row 3 (odd)
    [2,3,4,5,6,7,8],       // row 4 (even)
    [2,3,4,5,6,7],         // row 5 (odd)
    [3,4,5,6,7],           // row 6 (even)
    [3,4,5,6],             // row 7 (odd)
    [4,5,6],               // row 8 (even)
    [4,5],                 // row 9 (odd)
    [5],                   // row 10 (even)
  ],
  // 3: V 形 — 两侧向下合拢
  [
    [1,2,8,9],             // row 0 (even) — 左右两臂
    [1,2,7,8],             // row 1 (odd)
    [2,3,7,8],             // row 2 (even)
    [2,3,6,7],             // row 3 (odd)
    [3,4,6,7],             // row 4 (even)
    [3,4,5,6],             // row 5 (odd)
    [4,5,6],               // row 6 (even)
    [4,5],                 // row 7 (odd)
  ],
  // 4: 沙漏 ⧗ — 上下宽，中间细
  [
    [1,2,3,4,5,6,7,8,9],   // row 0 (even)
    [1,2,3,4,5,6,7,8],     // row 1 (odd)
    [2,3,4,5,6,7,8],       // row 2 (even)
    [3,4,5,6],             // row 3 (odd) — 腰部收窄
    [4,5,6],               // row 4 (even) — 最细处
    [3,4,5,6],             // row 5 (odd) — 开始放宽
    [2,3,4,5,6,7,8],       // row 6 (even)
    [1,2,3,4,5,6,7,8],     // row 7 (odd)
    [1,2,3,4,5,6,7,8,9],   // row 8 (even)
  ],
  // 5: 拱形 ∩ — 顶部圆弧，两侧向下延伸，中间镂空
  [
    [3,4,5,6,7],           // row 0 (even) — 拱顶
    [2,3,4,5,6,7],         // row 1 (odd)
    [1,2,3,4,5,6,7,8,9],   // row 2 (even) — 最宽处（连接左右两侧）
    [1,2,7,8],             // row 3 (odd)  — 两侧柱体，中间镂空
    [1,2,8,9],             // row 4 (even)
    [1,2,7,8],             // row 5 (odd)
    [1,2,8,9],             // row 6 (even)
  ],
];

/**
 * Voronoi 多源同步 BFS 着色：
 * 随机选取 colorCount 个种子点（各对应一种颜色），所有种子同时向外扩张，
 * 先到的染色。结果：每种颜色恰好是一块完全连通的区域，颜色之间紧密相邻，
 * 绝不会出现同色散落在不同位置的情况。
 *
 * @param {Array<[number,number]>} cells  所有待着色格子 [[row,col],...]
 * @param {number} colorCount 颜色种类数
 * @param {Function} rng      伪随机函数（种子化，保证可复现）
 * @returns {Map<string, number>}  key="row,col", value=颜色(1-based)
 */
function generateClusteredColorMap(cells, colorCount, rng) {
  const colorMap = new Map();
  if (cells.length === 0) return colorMap;

  // 六边形邻居 key 列表
  function hexNeighborKeys(row, col) {
    const ns = [[row, col - 1], [row, col + 1]];
    if (row % 2 === 0) {
      ns.push([row - 1, col - 1], [row - 1, col], [row + 1, col - 1], [row + 1, col]);
    } else {
      ns.push([row - 1, col], [row - 1, col + 1], [row + 1, col], [row + 1, col + 1]);
    }
    return ns.map(([r, c]) => `${r},${c}`);
  }

  const validSet = new Set(cells.map(([r, c]) => `${r},${c}`));

  // Fisher-Yates 洗牌，随机分散种子位置
  const shuffled = cells.slice();
  for (let i = shuffled.length - 1; i > 0; i--) {
    const j = Math.floor(rng() * (i + 1));
    const tmp = shuffled[i]; shuffled[i] = shuffled[j]; shuffled[j] = tmp;
  }

  // 从洗牌后的格子中，均匀间隔选取 colorCount 个种子（各占一种颜色）
  const step = Math.max(1, Math.floor(shuffled.length / colorCount));
  const bfsQueue = [];
  for (let i = 0; i < colorCount; i++) {
    const [r, c] = shuffled[Math.min(i * step, shuffled.length - 1)];
    const key = `${r},${c}`;
    if (!colorMap.has(key)) {
      colorMap.set(key, i + 1);
      bfsQueue.push([r, c]);
    }
  }

  // 所有种子同步 BFS 扩张（先入先染，形成 Voronoi 分区）
  while (bfsQueue.length > 0) {
    const [r, c] = bfsQueue.shift();
    const color = colorMap.get(`${r},${c}`);
    for (const nk of hexNeighborKeys(r, c)) {
      if (validSet.has(nk) && !colorMap.has(nk)) {
        colorMap.set(nk, color);
        const [nr, nc] = nk.split(',').map(Number);
        bfsQueue.push([nr, nc]);
      }
    }
  }

  return colorMap;
}

/**
 * 泡泡网格管理类
 * grid[row][col] 存储 Bubble 实例，null 表示空格
 */
export default class BubbleGrid {
  /**
   * @param {number} roomId 房间 ID，相同 ID 生成相同初始布局
   */
  constructor(roomId = 0) {
    this.grid = [];
    // 持久化 rng 实例：initGrid + 后续 pushTwoRows 共享同一序列
    this.rng = seededRng(roomId);
    // 下推动画偏移量（负值表示整体上移，每帧 ease-out 趋近 0）
    this._pushAnimOffsetY = 0;
    this.initGrid();
  }

  /**
   * 初始化泡泡网格，从 SHAPE_TEMPLATES 中用种子 RNG 选择一种造型。
   * 同一 roomId 始终选择同一模板并生成相同配色，保证多人同房公平。
   * 每个模板只在特定的格子放泡泡，形成心形/菱形/V形等视觉造型。
   */
  initGrid() {
    this.grid = [];
    const colorCount = getActiveColorCount(0);

    // 第一次 rng 调用决定形状，后续 rng 调用用于洪水填充着色
    const shapeIdx = Math.floor(this.rng() * SHAPE_TEMPLATES.length);
    const template = SHAPE_TEMPLATES[shapeIdx];

    // 模板基于 11/10 列设计，列数更多时把模板居中偏移
    const evenShift = Math.floor((EVEN_ROW_COLS - 11) / 2);
    const oddShift  = Math.floor((ODD_ROW_COLS - 10) / 2);

    // 收集模板中所有有效格子，一次性生成连通色块 Map
    const cells = [];
    for (let row = 0; row < template.length; row++) {
      const colCount = row % 2 === 0 ? EVEN_ROW_COLS : ODD_ROW_COLS;
      const shift = row % 2 === 0 ? evenShift : oddShift;
      for (const col of template[row]) {
        const mapped = col + shift;
        if (mapped >= 0 && mapped < colCount) cells.push([row, mapped]);
      }
    }
    const colorMap = generateClusteredColorMap(cells, colorCount, this.rng);

    for (let row = 0; row < template.length; row++) {
      const colCount = row % 2 === 0 ? EVEN_ROW_COLS : ODD_ROW_COLS;
      const shift = row % 2 === 0 ? evenShift : oddShift;
      const rowArr = new Array(colCount).fill(null);
      for (const col of template[row]) {
        const mapped = col + shift;
        if (mapped >= 0 && mapped < colCount) {
          const color = colorMap.get(`${row},${mapped}`) || 1;
          rowArr[mapped] = new Bubble(row, mapped, color);
        }
      }
      this.grid.push(rowArr);
    }
  }

  /**
   * 获取指定位置的泡泡，越界或不存在返回 null
   */
  getBubble(row, col) {
    if (row < 0 || row >= this.grid.length) return null;
    const rowArr = this.grid[row];
    if (!rowArr || col < 0 || col >= rowArr.length) return null;
    return rowArr[col] || null;
  }

  /**
   * 设置指定位置的泡泡
   * 如果行不存在则自动扩展 grid
   */
  setBubble(row, col, bubble) {
    while (this.grid.length <= row) {
      this.grid.push([]);
    }
    this.grid[row][col] = bubble;
  }

  /**
   * 返回六边形网格中 (row, col) 的最多 6 个相邻格坐标
   * 偏移规则：偶数行左移，奇数行右移
   */
  getNeighbors(row, col) {
    const neighbors = [
      [row, col - 1],
      [row, col + 1],
    ];
    if (row % 2 === 0) {
      neighbors.push([row - 1, col - 1], [row - 1, col]);
      neighbors.push([row + 1, col - 1], [row + 1, col]);
    } else {
      neighbors.push([row - 1, col], [row - 1, col + 1]);
      neighbors.push([row + 1, col], [row + 1, col + 1]);
    }
    return neighbors;
  }

  // ─── 碰撞与吸附 ───────────────────────────────────────────

  /**
   * 将屏幕坐标 (x, y) 吸附到最近的空格，创建并放入 Bubble，返回该 Bubble。
   * 若找不到合适的空格（极端情况）则返回 null。
   */
  snapToGrid(x, y, color) {
    const R = BUBBLE_RADIUS;
    const rowGuess = Math.round((y - R - SAFE_AREA_TOP) / ROW_HEIGHT);

    let bestRow = -1;
    let bestCol = -1;
    let bestDist = Infinity;

    for (let dr = -1; dr <= 1; dr++) {
      const row = rowGuess + dr;
      if (row < 0) continue;
      const colCount = row % 2 === 0 ? EVEN_ROW_COLS : ODD_ROW_COLS;

      for (let col = 0; col < colCount; col++) {
        if (this.getBubble(row, col)) continue;
        const pos = gridToScreen(row, col);
        const dist = Math.hypot(pos.x - x, pos.y - y);
        if (dist < bestDist) {
          bestDist = dist;
          bestRow = row;
          bestCol = col;
        }
      }
    }

    if (bestRow < 0) return null;

    const bubble = new Bubble(bestRow, bestCol, color);
    this.setBubble(bestRow, bestCol, bubble);
    return bubble;
  }

  // ─── 连通分析 ──────────────────────────────────────────────

  /**
   * BFS：返回从 (row, col) 出发所有连通的同色泡泡坐标数组 [[row, col], ...]
   */
  findConnected(row, col) {
    const startBubble = this.getBubble(row, col);
    if (!startBubble) return [];

    const targetColor = startBubble.color;
    const visited = new Set();
    const queue = [[row, col]];
    const result = [];

    visited.add(`${row},${col}`);

    while (queue.length > 0) {
      const [r, c] = queue.shift();
      result.push([r, c]);

      for (const [nr, nc] of this.getNeighbors(r, c)) {
        const key = `${nr},${nc}`;
        if (visited.has(key)) continue;
        const neighbor = this.getBubble(nr, nc);
        if (neighbor && neighbor.color === targetColor) {
          visited.add(key);
          queue.push([nr, nc]);
        }
      }
    }

    return result;
  }

  /**
   * BFS from row=0：返回所有"悬空"（与顶部不连通）泡泡的坐标数组
   */
  findFloating() {
    const visited = new Set();
    const queue = [];

    const firstRow = this.grid[0] || [];
    for (let col = 0; col < firstRow.length; col++) {
      if (firstRow[col]) {
        const key = `0,${col}`;
        visited.add(key);
        queue.push([0, col]);
      }
    }

    while (queue.length > 0) {
      const [r, c] = queue.shift();
      for (const [nr, nc] of this.getNeighbors(r, c)) {
        const key = `${nr},${nc}`;
        if (!visited.has(key) && this.getBubble(nr, nc)) {
          visited.add(key);
          queue.push([nr, nc]);
        }
      }
    }

    const floating = [];
    for (let row = 0; row < this.grid.length; row++) {
      const rowArr = this.grid[row];
      if (!rowArr) continue;
      for (let col = 0; col < rowArr.length; col++) {
        if (rowArr[col] && !visited.has(`${row},${col}`)) {
          floating.push([row, col]);
        }
      }
    }

    return floating;
  }

  // ─── 消除与掉落 ────────────────────────────────────────────

  /**
   * 从网格中删除指定的一组泡泡并加分（每颗 10 分）。
   * 仅在 cells.length >= MIN_POP_COUNT 时才实际消除，返回是否成功消除。
   */
  clearGroup(cells) {
    if (cells.length < MIN_POP_COUNT) return false;
    for (const [row, col] of cells) {
      if (this.grid[row]) this.grid[row][col] = null;
    }
    GameGlobal.databus.score += cells.length * 10;
    return true;
  }

  /**
   * 从网格中删除悬空泡泡（每颗额外加 5 分）。
   */
  dropFloating(cells) {
    for (const [row, col] of cells) {
      if (this.grid[row]) this.grid[row][col] = null;
    }
    if (cells.length > 0) {
      GameGlobal.databus.score += cells.length * 5;
    }
  }

  // ─── 行补充（保持约 12 行活跃泡泡）─────────────────────────

  /**
   * 向顶部插入 2 行新泡泡，所有已有泡泡整体下移 2 行。
   * 移 2 行可保持奇偶对齐（偶数行仍是偶数行，列数不变）。
   * @param {number} colorCount 当前可用颜色种类数
   */
  pushTwoRows(colorCount) {
    const newGrid = [];

    // 收集两行所有格子，生成连通色块 Map
    const newCells = [];
    for (let col = 0; col < EVEN_ROW_COLS; col++) newCells.push([0, col]);
    for (let col = 0; col < ODD_ROW_COLS; col++) newCells.push([1, col]);
    const colorMap = generateClusteredColorMap(newCells, colorCount, this.rng);

    // 生成新的第 0 行（偶数行，11 列）
    const row0 = [];
    for (let col = 0; col < EVEN_ROW_COLS; col++) {
      const color = colorMap.get(`0,${col}`) || 1;
      row0.push(new Bubble(0, col, color));
    }
    // 生成新的第 1 行（奇数行，10 列）
    const row1 = [];
    for (let col = 0; col < ODD_ROW_COLS; col++) {
      const color = colorMap.get(`1,${col}`) || 1;
      row1.push(new Bubble(1, col, color));
    }
    newGrid.push(row0, row1);

    // 将原有所有行下移 2，同时更新 Bubble 的行坐标与屏幕坐标
    for (let oldRow = 0; oldRow < this.grid.length; oldRow++) {
      const newRowIdx = oldRow + 2;
      const oldRowArr = this.grid[oldRow] || [];
      // 奇偶不变，列数也不变
      const colCount = newRowIdx % 2 === 0 ? EVEN_ROW_COLS : ODD_ROW_COLS;
      const newRowArr = new Array(colCount).fill(null);
      for (let col = 0; col < oldRowArr.length && col < colCount; col++) {
        const bubble = oldRowArr[col];
        if (bubble) {
          bubble.moveTo(newRowIdx, col);
          newRowArr[col] = bubble;
        }
      }
      newGrid.push(newRowArr);
    }

    this.grid = newGrid;

    // 触发下推动画：让所有泡泡视觉上从旧位置滑向新位置
    this._pushAnimOffsetY -= ROW_HEIGHT * 2;
  }

  /**
   * 每帧调用：ease-out 逼近偏移量归零，驱动下推动画
   */
  update() {
    if (this._pushAnimOffsetY < 0) {
      this._pushAnimOffsetY += (0 - this._pushAnimOffsetY) * 0.18;
      if (this._pushAnimOffsetY > -0.5) this._pushAnimOffsetY = 0;
    }
  }

  /**
   * 返回含有至少一个泡泡的行数
   */
  getLiveRowCount() {
    return this.grid.filter(rowArr => rowArr && rowArr.some(b => b !== null)).length;
  }

  // ─── 游戏结束判定 ──────────────────────────────────────────

  /**
   * 检查是否有泡泡超过危险线（进入射击器上方的禁区）。
   * 危险线 = 鸡头顶位置上方 1 个泡泡直径处（与射击器实际位置关联）。
   */
  isGameOver() {
    const R = BUBBLE_RADIUS;
    // 危险线 = 鸡头顶上方约 1 行泡泡高度
    // 鸡占 6R 高 + 底部边距 0.8R + 泡泡偏移 0.4R + 额外 4R 缓冲 = 11.2R
    const dangerY = SCREEN_HEIGHT - SAFE_AREA_BOTTOM - R * 11.2;

    for (let row = 0; row < this.grid.length; row++) {
      const rowArr = this.grid[row];
      if (!rowArr) continue;
      for (let col = 0; col < rowArr.length; col++) {
        if (!rowArr[col]) continue;
        const { y } = gridToScreen(row, col);
        if (y > dangerY) return true;
      }
    }
    return false;
  }

  /**
   * 遍历所有格子，渲染每个非 null 泡泡。
   * 若下推动画正在播放，将每个泡泡的渲染 Y 坐标叠加偏移量，
   * 造成整体从旧位置滑向新位置的视觉效果。
   */
  render(ctx) {
    const offsetY = this._pushAnimOffsetY;
    for (let row = 0; row < this.grid.length; row++) {
      const rowArr = this.grid[row];
      if (!rowArr) continue;
      for (let col = 0; col < rowArr.length; col++) {
        const bubble = rowArr[col];
        if (!bubble || !bubble.visible || bubble.color === 0) continue;
        if (offsetY === 0) {
          bubble.render(ctx);
        } else {
          drawBubble3D(ctx, bubble.x, bubble.y + offsetY, BUBBLE_RADIUS, bubble.color);
        }
      }
    }

    // 绘制危险线（调试用）
    const R = BUBBLE_RADIUS;
    const dangerY = SCREEN_HEIGHT - SAFE_AREA_BOTTOM - R * 11.2;
    ctx.save();
    ctx.strokeStyle = 'rgba(255, 0, 0, 0.7)';
    ctx.lineWidth = 2;
    ctx.setLineDash([8, 4]);
    ctx.beginPath();
    ctx.moveTo(0, dangerY);
    ctx.lineTo(SCREEN_WIDTH, dangerY);
    ctx.stroke();
    ctx.setLineDash([]);
    ctx.restore();
  }
}

export { MIN_LIVE_ROWS };
