"use client";
// 回响星核 / Echo Nexus — Zustand 游戏状态管理
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
import type {
  GameState,
  Crystal,
  CrystalTier,
  DecodePuzzle,
  ExpeditionResult,
} from "@/lib/game/types";
import {
  INITIAL_STATE,
  TECH_TREE,
  CRYSTAL_VALUE,
  CONTACT,
  CRYSTAL_SPAWN,
  FRAGMENTS,
  PRESTIGE,
} from "@/lib/game/config";
import {
  createInitialState,
  recomputeStats,
  decodeRewards,
  rollCrystalTier,
  computeNewBlueprints,
  performPrestige,
} from "@/lib/game/engine";
import {
  generatePuzzle,
  tryClickNode,
  isSolvable,
  resetPuzzle as resetPuz,
} from "@/lib/game/decode";
import {
  generateExpedition,
  resolveNode,
  advanceExpedition,
  computeExpeditionPower,
  computeExpeditionHp,
  computeEnergyRegen,
  EXPEDITION_CONFIG,
} from "@/lib/game/expedition";
import { ACHIEVEMENTS, type Achievement } from "@/lib/game/achievements";

interface GameActions {
  // 生命周期
  init: () => void;
  loadOnline: () => void;
  hardReset: () => void;

  // 主循环
  tick: (now: number) => void;
  pulse: () => { gain: number; combo: number } | null;

  // 解码
  startDecode: (crystalId: string) => void;
  clickNode: (nodeId: number) => { ok: boolean; finished: boolean; solvable?: boolean; failReason?: string };
  undoStep: () => void;
  retryPuzzle: () => void;
  abandonPuzzle: () => void;
  /** 自动解码 T1（技术解锁后由 tick 调用） */
  autoDecodeTick: () => void;

  // 探险
  startExpedition: () => { ok: boolean; reason?: string };
  resolveCurrentNode: () => ExpeditionResult | null;
  advanceNode: () => void;
  abortExpedition: () => void;

  // 技术
  buyTech: (techId: string) => boolean;

  // 飞升
  doPrestige: () => { newBp: number } | null;

  // 成就
  checkAchievements: () => Achievement[];
  consumeAchievementQueue: () => Achievement[];

  // 设置
  toggleTheme: () => void;
  toggleSound: () => void;

  // 派生
  canPrestige: () => boolean;
}

type Store = GameState & GameActions & {
  _lastAutoDecode: number;
  _lastSpawn: number;
  _combo: number;
  _lastPulse: number;
  _achievementQueue: Achievement[];
};

/** 计算并写回产能字段 */
function syncStats(state: Partial<GameState>) {
  const s = recomputeStats(state);
  return {
    crystalsPerSec: s.crystalsPerSec,
    crystalCap: s.crystalCap,
    pulsePower: s.pulsePower,
    offlineEff: s.offlineEff,
    insightMult: s.insightMult,
    contactRateMult: s.contactRateMult,
    autoDecode: s.autoDecode,
  };
}

/** 检查并解锁叙事碎片 */
function checkFragments(state: GameState): string[] {
  const unlocked: string[] = [];
  for (const f of FRAGMENTS) {
    if (!state.fragments[f.id] && state.totalDecoded >= f.threshold) {
      state.fragments[f.id] = true;
      unlocked.push(f.id);
    }
  }
  return unlocked;
}

export const useGameStore = create<Store>()(
  persist(
    (set, get) => ({
      ...createInitialState(),
      _lastAutoDecode: Date.now(),
      _lastSpawn: Date.now(),
      _combo: 0,
      _lastPulse: 0,
      _achievementQueue: [],

      init: () => {
        const s = get();
        const now = Date.now();
        // 兼容旧存档：若存在不可解的活跃谜题（旧算法生成），直接清掉
        let activePuzzle = s.activePuzzle;
        if (activePuzzle && !isSolvable(activePuzzle)) {
          // 把晶体放回队列，避免玩家卡死
          const crystal: Crystal = {
            id: `c_${now}_rec`,
            tier: activePuzzle.tier,
            value: CRYSTAL_VALUE[activePuzzle.tier].crystals,
            createdAt: now,
          };
          activePuzzle = null;
          set({
            pendingCrystals: [...s.pendingCrystals, crystal].slice(-CRYSTAL_SPAWN.maxPending),
          });
        }
        // 兼容旧存档：补全 achievements 字段
        const achievements = s.achievements ?? {};
        // 首次进入：补发离线收益
        const elapsed = Math.max(0, (now - s.lastTick) / 1000);
        if (elapsed > 5) {
          const cap = 8 * 3600;
          const secs = Math.min(elapsed, cap);
          const gain = s.crystalsPerSec * secs * s.offlineEff;
          set({
            crystals: Math.min(s.crystalCap, s.crystals + gain),
            lastTick: now,
            activePuzzle,
            achievements,
            ...syncStats({ tech: s.tech, blueprints: s.blueprints, achievements }),
          });
        } else {
          set({ lastTick: now, activePuzzle, achievements, ...syncStats({ tech: s.tech, blueprints: s.blueprints, achievements }) });
        }
      },

      loadOnline: () => {
        const s = get();
        set({ ...syncStats({ tech: s.tech, blueprints: s.blueprints, achievements: s.achievements }) });
      },

      hardReset: () => {
        set({ ...createInitialState(), _lastAutoDecode: Date.now(), _lastSpawn: Date.now(), _combo: 0, _lastPulse: 0, _achievementQueue: [] });
      },

      tick: (now) => {
        const s = get();
        const dt = Math.max(0, (now - s.lastTick) / 1000);
        if (dt <= 0) return;

        // 产能累加（仅闲置产能受仓库上限；探险奖励可超出）
        const newCrystals =
          s.crystals >= s.crystalCap
            ? s.crystals // 已达/超上限，不再自动产出
            : Math.min(s.crystalCap, s.crystals + s.crystalsPerSec * dt);

        // 自动产出待解码晶体（每 baseInterval 秒一颗，受技术/飞升略提速）
        const bpBoost = 1 + s.blueprints.length * 0.03;
        const spawnInterval = (CRYSTAL_SPAWN.baseIntervalSec / bpBoost) * 1000;
        let pending = s.pendingCrystals;
        let lastSpawn = s._lastSpawn;
        if (
          now - lastSpawn > spawnInterval &&
          pending.length < CRYSTAL_SPAWN.maxPending
        ) {
          const tier = rollCrystalTier();
          const crystal: Crystal = {
            id: `c_${now}_${Math.random().toString(36).slice(2, 7)}`,
            tier,
            value: CRYSTAL_VALUE[tier].crystals,
            createdAt: now,
          };
          pending = [...pending, crystal];
          lastSpawn = now;
        }

        // 能量恢复（探险系统）
        let energy = s.energy;
        let lastEnergyTick = s.lastEnergyTick;
        if (energy < s.energyMax) {
          const regen = computeEnergyRegen(lastEnergyTick, now, energy, s.energyMax);
          energy = regen.energy;
          lastEnergyTick = regen.lastTick;
        } else {
          lastEnergyTick = now;
        }

        set({
          crystals: newCrystals,
          lastTick: now,
          pendingCrystals: pending,
          _lastSpawn: lastSpawn,
          energy,
          lastEnergyTick,
        });
      },

      pulse: () => {
        const s = get();
        const now = Date.now();
        // 连击
        let combo = 1;
        if (now - s._lastPulse < 1500) {
          combo = Math.min(10, s._combo + 1);
        }
        const mult = 1 + (combo - 1) * 0.15;
        const gain = s.pulsePower * mult;
        set({
          crystals: Math.min(s.crystalCap, s.crystals + gain),
          _combo: combo,
          _lastPulse: now,
        });
        return { gain, combo };
      },

      startDecode: (crystalId) => {
        const s = get();
        const crystal = s.pendingCrystals.find((c) => c.id === crystalId);
        if (!crystal) return;
        const puzzle = generatePuzzle(crystal.tier);
        set({
          activePuzzle: puzzle,
          pendingCrystals: s.pendingCrystals.filter((c) => c.id !== crystalId),
        });
      },

      clickNode: (nodeId) => {
        const s = get();
        if (!s.activePuzzle) return { ok: false, finished: false, failReason: "无活跃谜题" };
        // 深拷贝谜题
        const puzzle: DecodePuzzle = JSON.parse(JSON.stringify(s.activePuzzle));
        const res = tryClickNode(puzzle, nodeId);
        if (res.ok) {
          if (res.finished) {
            // 结算奖励
            const rewards = decodeRewards(puzzle.tier, s.insightMult, s.contactRateMult);
            const newTotal = s.totalDecoded + 1;
            const newContact = Math.min(100, s.contact + rewards.contact);
            const newInsights = s.insights + rewards.insights;
            const newCrystals = s.crystals + rewards.crystals;
            // 解锁碎片
            const tentative: GameState = { ...s, totalDecoded: newTotal, fragments: { ...s.fragments } };
            const unlocked = checkFragments(tentative);
            set({
              activePuzzle: null,
              crystals: newCrystals,
              insights: newInsights,
              contact: newContact,
              totalDecoded: newTotal,
              fragments: tentative.fragments,
            });
            return { ok: true, finished: true, failReason: unlocked.join(",") || undefined };
          }
          // 点击成功但未完成：检测当前局面是否仍可解
          const solvable = isSolvable(puzzle);
          set({ activePuzzle: puzzle });
          return { ok: true, finished: false, solvable };
        }
        return res;
      },

      undoStep: () => {
        const s = get();
        if (!s.activePuzzle || s.activePuzzle.path.length === 0) return;
        const puzzle: DecodePuzzle = JSON.parse(JSON.stringify(s.activePuzzle));
        const lastId = puzzle.path.pop();
        if (lastId !== undefined) {
          const node = puzzle.grid.find((n) => n.id === lastId);
          if (node) node.used = false;
        }
        set({ activePuzzle: puzzle });
      },

      retryPuzzle: () => {
        const s = get();
        if (!s.activePuzzle) return;
        set({ activePuzzle: resetPuz(s.activePuzzle) });
      },

      abandonPuzzle: () => {
        const s = get();
        if (!s.activePuzzle) return;
        // 晶体放回队列末尾
        const crystal: Crystal = {
          id: `c_${Date.now()}_ret`,
          tier: s.activePuzzle.tier,
          value: CRYSTAL_VALUE[s.activePuzzle.tier].crystals,
          createdAt: Date.now(),
        };
        set({
          activePuzzle: null,
          pendingCrystals: [...s.pendingCrystals, crystal].slice(-CRYSTAL_SPAWN.maxPending),
        });
      },

      autoDecodeTick: () => {
        const s = get();
        if (!s.autoDecode) return;
        const now = Date.now();
        if (now - s._lastAutoDecode < 12000) return;
        // 找一颗 T1 晶体自动解码
        const idx = s.pendingCrystals.findIndex((c) => c.tier === 1);
        if (idx < 0) return;
        const crystal = s.pendingCrystals[idx];
        const rewards = decodeRewards(1, s.insightMult, s.contactRateMult);
        const newTotal = s.totalDecoded + 1;
        const tentative: GameState = { ...s, totalDecoded: newTotal, fragments: { ...s.fragments } };
        checkFragments(tentative);
        set({
          pendingCrystals: s.pendingCrystals.filter((c) => c.id !== crystal.id),
          crystals: s.crystals + rewards.crystals,
          insights: s.insights + rewards.insights,
          contact: Math.min(100, s.contact + rewards.contact),
          totalDecoded: newTotal,
          fragments: tentative.fragments,
          _lastAutoDecode: now,
        });
      },

      buyTech: (techId) => {
        const s = get();
        const node = TECH_TREE.find((t) => t.id === techId);
        if (!node) return false;
        const cur = s.tech[techId] ?? 0;
        if (cur >= 1) return false; // v0.1 每节点 1 级
        if (s.insights < node.cost) return false;
        const newTech = { ...s.tech, [techId]: 1 };
        set({
          insights: s.insights - node.cost,
          tech: newTech,
          ...syncStats({ tech: newTech, blueprints: s.blueprints }),
        });
        return true;
      },

      // ============ 探险系统 ============
      startExpedition: () => {
        const s = get();
        if (s.activeExpedition && !s.activeExpedition.finished) {
          return { ok: false, reason: "已有进行中的探险" };
        }
        if (s.energy < EXPEDITION_CONFIG.energyCost) {
          return { ok: false, reason: "能量不足" };
        }
        const power = computeExpeditionPower(s);
        const hp = computeExpeditionHp(s);
        const seed = Math.floor(Math.random() * 1e9);
        const expedition = generateExpedition(seed, power, hp);
        set({
          activeExpedition: expedition,
          energy: s.energy - EXPEDITION_CONFIG.energyCost,
          totalExpeditions: s.totalExpeditions + 1,
        });
        return { ok: true };
      },

      resolveCurrentNode: () => {
        const s = get();
        if (!s.activeExpedition || s.activeExpedition.finished) return null;
        // 深拷贝
        const exp = JSON.parse(JSON.stringify(s.activeExpedition));
        const result = resolveNode(exp);
        // 累计奖励
        if (result.crystals) exp.rewards.crystals += result.crystals;
        if (result.insights) exp.rewards.insights += result.insights;
        if (result.contact) exp.rewards.contact += result.contact;
        if (result.fragments) exp.rewards.fragments.push(...result.fragments);
        if (result.hpDelta) exp.hp = Math.max(0, Math.min(exp.maxHp, exp.hp + result.hpDelta));
        // 实时入账（玩家立即获得）
        const newCrystals = s.crystals + (result.crystals || 0);
        const newInsights = s.insights + (result.insights || 0);
        const newContact = Math.min(100, s.contact + (result.contact || 0));
        // 碎片解锁
        const newFragments = { ...s.fragments };
        if (result.fragments) {
          for (const fid of result.fragments) newFragments[fid] = true;
        }
        // 日志
        const logEntry = {
          expeditionId: exp.id,
          nodeType: exp.nodes[exp.currentNode]?.type || "combat",
          result: result.log,
          rewards: [
            result.crystals ? `+${result.crystals}晶体` : "",
            result.insights ? `+${result.insights}洞见` : "",
            result.contact ? `+${result.contact.toFixed(1)}接触` : "",
            result.hpDelta ? `${result.hpDelta > 0 ? "+" : ""}${result.hpDelta}生命` : "",
          ].filter(Boolean).join(" "),
          timestamp: Date.now(),
        };
        const newLog = [logEntry, ...s.expeditionLog].slice(0, 30);

        if (result.ended) {
          // 探险结束（胜利或失败）
          exp.finished = true;
        }

        set({
          activeExpedition: exp,
          crystals: newCrystals,
          insights: newInsights,
          contact: newContact,
          fragments: newFragments,
          expeditionLog: newLog,
        });
        return result;
      },

      advanceNode: () => {
        const s = get();
        if (!s.activeExpedition || s.activeExpedition.finished) return;
        const exp = JSON.parse(JSON.stringify(s.activeExpedition));
        const node = exp.nodes[exp.currentNode];
        if (!node || !node.cleared) return; // 当前节点未结算不能前进
        if (exp.currentNode >= exp.nodes.length - 1) return;
        exp.currentNode++;
        set({ activeExpedition: exp });
      },

      abortExpedition: () => {
        const s = get();
        if (!s.activeExpedition) return;
        const exp = JSON.parse(JSON.stringify(s.activeExpedition));
        exp.finished = true;
        const logEntry = {
          expeditionId: exp.id,
          nodeType: "rest" as const,
          result: "探险队主动撤退，保留已获奖励。",
          rewards: "",
          timestamp: Date.now(),
        };
        set({
          activeExpedition: exp,
          expeditionLog: [logEntry, ...s.expeditionLog].slice(0, 30),
        });
      },

      doPrestige: () => {
        const s = get();
        if (s.contact < CONTACT.prestigeMin) return null;
        const newBp = computeNewBlueprints(s);
        const next = performPrestige(s);
        set({
          ...next,
          ...syncStats({ tech: next.tech, blueprints: next.blueprints, achievements: next.achievements }),
          _lastAutoDecode: Date.now(),
          _lastSpawn: Date.now(),
          _combo: 0,
          _lastPulse: 0,
        });
        return { newBp };
      },

      checkAchievements: () => {
        const s = get();
        const newlyUnlocked: Achievement[] = [];
        const updated = { ...s.achievements };
        let crystals = s.crystals;
        let insights = s.insights;
        let contact = s.contact;
        let statsDirty = false;
        for (const a of ACHIEVEMENTS) {
          if (updated[a.id]) continue;
          if (a.check(s)) {
            updated[a.id] = true;
            newlyUnlocked.push(a);
            // 发放即时奖励
            if (a.reward.crystals) crystals += a.reward.crystals;
            if (a.reward.insights) insights += a.reward.insights;
            if (a.reward.contact) contact = Math.min(100, contact + a.reward.contact);
            if (a.reward.crystalsPerSecPct || a.reward.insightPct) statsDirty = true;
          }
        }
        if (newlyUnlocked.length === 0) return [];
        set({
          achievements: updated,
          crystals,
          insights,
          contact,
          ...(statsDirty
            ? syncStats({ tech: s.tech, blueprints: s.blueprints, achievements: updated })
            : {}),
          _achievementQueue: [...s._achievementQueue, ...newlyUnlocked],
        });
        return newlyUnlocked;
      },

      consumeAchievementQueue: () => {
        const s = get();
        if (s._achievementQueue.length === 0) return [];
        const items = s._achievementQueue;
        set({ _achievementQueue: [] });
        return items;
      },

      canPrestige: () => get().contact >= CONTACT.prestigeMin,

      toggleTheme: () => set({ theme: get().theme === "dark" ? "light" : "dark" }),
      toggleSound: () => set({ soundOn: !get().soundOn }),
    }),
    {
      name: "echo-nexus-save-v1",
      storage: createJSONStorage(() => localStorage),
      // 不持久化临时字段
      partialize: (s) => {
        const { _lastAutoDecode, _lastSpawn, _combo, _lastPulse, _achievementQueue, ...rest } = s;
        void _lastAutoDecode; void _lastSpawn; void _combo; void _lastPulse; void _achievementQueue;
        return rest as GameState;
      },
    }
  )
);

/** 选择器：未解锁碎片中下一个门槛 */
export function nextFragmentThreshold(totalDecoded: number): number | null {
  for (const f of FRAGMENTS) {
    if (totalDecoded < f.threshold) return f.threshold;
  }
  return null;
}

export { FRAGMENTS, PRESTIGE, TECH_TREE };
