// 回响星核 / Echo Nexus — 解码共振谜题逻辑（原创核心玩法）
//
// 谜题规则「谐振序列 / Harmonic Sequence」：
// 1. 一个 rows×cols 的发光节点阵列，每个节点带一种共振色（4 色全息色谱）。
// 2. 中央给出「目标谐振序列」target: ResonanceColor[]（长度由 tier 决定）。
// 3. 玩家依次点击节点，重建该序列：
//    - 第 1 次点击可任意节点，但该节点颜色须 === target[0]。
//    - 第 k 次点击的节点颜色须 === target[k-1]，且与上一次点击节点**四邻接**（上下左右）。
//    - 每个节点只能用一次（点击后变暗）。
// 4. 步数上限 = stepLimitBase + 解码技术加成；超出则谐振崩塌，可重试（晶体不损耗）。
// 5. 成功 → 释放晶体奖励 + 洞见 + 接触进度 + 可能触发叙事碎片。
//
// 设计意图：把放置游戏的「等待」变成有节奏的空间规划小谜题，兼具策略与爽快感。

import type {
  CrystalTier,
  DecodeNode,
  DecodePuzzle,
  ResonanceColor,
} from "./types";
import { DECODE_CONFIG, RESONANCE_COLORS } from "./config";

/** 简单可复现随机（mulberry32） */
function makeRng(seed: number) {
  let a = seed >>> 0;
  return () => {
    a |= 0;
    a = (a + 0x6d2b79f5) | 0;
    let t = Math.imul(a ^ (a >>> 15), 1 | a);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}

/**
 * 生成一个**保证有解**的解码谜题。
 *
 * 算法（路径构造法）：
 * 1. 先随机生成目标序列 target。
 * 2. 在网格上**主动构造一条合法路径**：随机起点，每步走向一个未使用的四邻接节点，
 *    长度 = target.length。构造成功后，把路径上第 i 个节点颜色**强制设为 target[i]**。
 * 3. 其余非路径节点随机填色（偏向目标色，提升视觉丰富度与多解性）。
 *
 * 这样至少存在一条解（即构造的路径），玩家不会遇到「怎么都过不了」的死局。
 * 同时由于其余节点随机，玩家可能发现别的解路径，保留探索乐趣。
 */
export function generatePuzzle(tier: CrystalTier, seed?: number): DecodePuzzle {
  const cfg = DECODE_CONFIG[tier];
  const s = seed ?? Math.floor(Math.random() * 1e9);
  const rng = makeRng(s);

  const { rows, cols, targetLen } = cfg;
  const total = rows * cols;

  // 目标序列：从 4 色中随机选 targetLen 个（允许重复，但保证至少 2 种色以增加策略）
  const target: ResonanceColor[] = [];
  for (let i = 0; i < targetLen; i++) {
    target.push(RESONANCE_COLORS[Math.floor(rng() * RESONANCE_COLORS.length)]);
  }
  if (new Set(target).size < 2) {
    target[target.length - 1] =
      RESONANCE_COLORS[(RESONANCE_COLORS.indexOf(target[0]) + 1) % RESONANCE_COLORS.length];
  }

  // 邻居计算
  const neighborsOf = (id: number): number[] => {
    const r = Math.floor(id / cols);
    const c = id % cols;
    const ns: number[] = [];
    if (r > 0) ns.push((r - 1) * cols + c);
    if (r < rows - 1) ns.push((r + 1) * cols + c);
    if (c > 0) ns.push(r * cols + (c - 1));
    if (c < cols - 1) ns.push(r * cols + (c + 1));
    return ns;
  };

  // 构造一条保证可解的路径（多次尝试）
  let pathIds: number[] | null = null;
  for (let attempt = 0; attempt < 300 && !pathIds; attempt++) {
    const used = new Set<number>();
    const path: number[] = [];
    const start = Math.floor(rng() * total);
    used.add(start);
    path.push(start);
    let cur = start;
    let ok = true;
    for (let i = 1; i < target.length; i++) {
      const ns = neighborsOf(cur).filter((n) => !used.has(n));
      if (ns.length === 0) {
        ok = false;
        break;
      }
      cur = ns[Math.floor(rng() * ns.length)];
      used.add(cur);
      path.push(cur);
    }
    if (ok) pathIds = path;
  }
  // 极端兜底（理论不会触发）：线性路径
  if (!pathIds) {
    pathIds = Array.from({ length: target.length }, (_, i) => i);
  }

  // 路径节点颜色映射
  const pathColor = new Map<number, ResonanceColor>();
  pathIds.forEach((id, i) => pathColor.set(id, target[i]));

  // 生成网格
  const grid: DecodeNode[] = [];
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      const id = r * cols + c;
      let color: ResonanceColor;
      if (pathColor.has(id)) {
        color = pathColor.get(id)!;
      } else {
        // 非路径节点：60% 偏向目标色（提升多解性），40% 随机全色
        if (rng() < 0.6) {
          color = target[Math.floor(rng() * target.length)];
        } else {
          color = RESONANCE_COLORS[Math.floor(rng() * RESONANCE_COLORS.length)];
        }
      }
      grid.push({ id, row: r, col: c, color, used: false });
    }
  }

  return {
    tier,
    grid,
    rows,
    cols,
    target,
    path: [],
    stepLimit: cfg.stepLimitBase,
    seed: s,
  };
}

/**
 * 精确判断当前局面是否仍可解。
 * 从当前路径末端出发，DFS 搜索是否存在一条按 target 剩余顺序、四邻接、不重复的完成路径。
 * 用于玩家点击后检测「此路不通」，提示撤销。
 */
export function isSolvable(puzzle: DecodePuzzle): boolean {
  const remaining = puzzle.target.slice(puzzle.path.length);
  if (remaining.length === 0) return true;

  const used = new Set(puzzle.path);
  const neighborsOf = (id: number): number[] => {
    const node = puzzle.grid.find((n) => n.id === id);
    if (!node) return [];
    const ns: number[] = [];
    if (node.row > 0) ns.push((node.row - 1) * puzzle.cols + node.col);
    if (node.row < puzzle.rows - 1) ns.push((node.row + 1) * puzzle.cols + node.col);
    if (node.col > 0) ns.push(node.row * puzzle.cols + (node.col - 1));
    if (node.col < puzzle.cols - 1) ns.push(node.row * puzzle.cols + (node.col + 1));
    return ns;
  };

  // 若尚未落子，任意颜色匹配 remaining[0] 的未使用节点都可作为起点
  const starts: number[] =
    puzzle.path.length === 0
      ? puzzle.grid.filter((n) => !used.has(n.id) && n.color === remaining[0]).map((n) => n.id)
      : [puzzle.path[puzzle.path.length - 1]];

  const dfs = (curId: number, idx: number): boolean => {
    if (idx >= remaining.length) return true;
    const nextColor = remaining[idx];
    for (const nid of neighborsOf(curId)) {
      if (used.has(nid)) continue;
      const n = puzzle.grid.find((x) => x.id === nid);
      if (!n || n.color !== nextColor) continue;
      // 起点情况：第一步 idx=0 时，curId 本身要匹配 nextColor
      if (idx === 0 && puzzle.path.length === 0) {
        const cur = puzzle.grid.find((x) => x.id === curId);
        if (!cur || cur.color !== nextColor) continue;
      }
      used.add(nid);
      if (dfs(nid, idx + 1)) return true;
      used.delete(nid);
    }
    return false;
  };

  for (const st of starts) {
    if (puzzle.path.length === 0) {
      // 起点自身需匹配 remaining[0]
      const cur = puzzle.grid.find((n) => n.id === st);
      if (!cur || cur.color !== remaining[0]) continue;
      used.add(st);
      if (dfs(st, 1)) return true;
      used.delete(st);
    } else {
      if (dfs(st, 0)) return true;
    }
  }
  return false;
}

/** 两节点是否四邻接 */
export function isAdjacent(a: DecodeNode, b: DecodeNode): boolean {
  return Math.abs(a.row - b.row) + Math.abs(a.col - b.col) === 1;
}

/** 尝试点击节点。返回 { ok, finished, failReason } */
export function tryClickNode(
  puzzle: DecodePuzzle,
  nodeId: number
): { ok: boolean; finished: boolean; failReason?: string } {
  const node = puzzle.grid.find((n) => n.id === nodeId);
  if (!node) return { ok: false, finished: false, failReason: "无效节点" };
  if (node.used) return { ok: false, finished: false, failReason: "节点已使用" };

  const stepIdx = puzzle.path.length;
  // 颜色须匹配目标序列当前位置
  if (node.color !== puzzle.target[stepIdx]) {
    return { ok: false, finished: false, failReason: "谐振不匹配" };
  }
  // 非首步须邻接
  if (stepIdx > 0) {
    const prev = puzzle.grid.find((n) => n.id === puzzle.path[stepIdx - 1])!;
    if (!isAdjacent(prev, node)) {
      return { ok: false, finished: false, failReason: "节点不相邻" };
    }
  }
  // 步数上限
  if (stepIdx >= puzzle.stepLimit) {
    return { ok: false, finished: false, failReason: "步数耗尽" };
  }

  node.used = true;
  puzzle.path.push(nodeId);

  const finished = puzzle.path.length >= puzzle.target.length;
  return { ok: true, finished };
}

/** 是否已无解 — 复用精确的 isSolvable 判定 */
export function isStuck(puzzle: DecodePuzzle): boolean {
  if (puzzle.path.length >= puzzle.target.length) return false;
  if (puzzle.path.length >= puzzle.stepLimit) return true;
  return !isSolvable(puzzle);
}

/**
 * 假设从某节点作为第一步落子，局面是否仍可解。
 * 用于在选起点阶段高亮「可行起点」，避免玩家选到死起点。
 */
export function canStartFrom(puzzle: DecodePuzzle, nodeId: number): boolean {
  if (puzzle.path.length !== 0) return false;
  const node = puzzle.grid.find((n) => n.id === nodeId);
  if (!node || node.used) return false;
  if (node.color !== puzzle.target[0]) return false;
  const cp: DecodePuzzle = JSON.parse(JSON.stringify(puzzle));
  const n = cp.grid.find((x) => x.id === nodeId)!;
  n.used = true;
  cp.path.push(nodeId);
  return isSolvable(cp);
}

/** 重置谜题（保留同一晶体，重新挑战） */
export function resetPuzzle(puzzle: DecodePuzzle): DecodePuzzle {
  return generatePuzzle(puzzle.tier, puzzle.seed + 1);
}
