import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import recipesData from '../data/recipes.json';
import elementsData from '../data/elements.json';
import prestigeTableData from '../data/prestige.json';
import achievementsData from '../data/achievements.json';

// localStorage 저장을 최대 10초에 1번으로 스로틀링
// 매 tick(1초)마다 setState가 발생하는 방치형 게임 특성상 필요
const STORAGE_SAVE_INTERVAL_MS = 10_000;

let _pendingStorageKey: string | null = null;
let _pendingStorageValue: string | null = null;
let _storageTimer: ReturnType<typeof setTimeout> | null = null;

function _doFlush() {
  if (_pendingStorageKey !== null && _pendingStorageValue !== null) {
    localStorage.setItem(_pendingStorageKey, _pendingStorageValue);
    _pendingStorageKey = null;
    _pendingStorageValue = null;
  }
  _storageTimer = null;
}

/** 탭 전환/앱 종료 직전 즉시 저장 (beforeunload에서 호출) */
export function flushGameState(): void {
  if (_storageTimer !== null) {
    clearTimeout(_storageTimer);
    _storageTimer = null;
  }
  _doFlush();
}

const throttledStorage = createJSONStorage(() => ({
  getItem: (name: string): string | null => localStorage.getItem(name),
  setItem: (name: string, value: string): void => {
    _pendingStorageKey = name;
    _pendingStorageValue = value;
    if (_storageTimer === null) {
      _storageTimer = setTimeout(_doFlush, STORAGE_SAVE_INTERVAL_MS);
    }
  },
  removeItem: (name: string): void => localStorage.removeItem(name),
}));

export type TabName = 'elements' | 'evolution' | 'fusion' | 'shop' | 'achievements' | 'settings';
export type Language = 'ko' | 'en';

const INITIAL_ELEMENTS: Record<string, number> = {
  fire: 5,
  water: 5,
  wind: 5,
  earth: 5,
};

export const ENHANCE_MAX_LEVEL = 5;
const ENHANCE_BASE_COST = 50;
const ENHANCE_COST_MULTIPLIER = 1.5;
const ENHANCE_RATE_MULTIPLIER = 1.2;
const SPAWN_ENHANCE_MULTIPLIER = 1.2;
const MAX_OFFLINE_SECONDS = 86400; // 24시간

export function calcEnhanceCost(level: number): number {
  return Math.floor(ENHANCE_BASE_COST * Math.pow(ENHANCE_COST_MULTIPLIER, level));
}

export function calcEffectiveIdleRate(elementId: string, level: number): number {
  const base = elementsData.find((e) => e.id === elementId)?.idleIncomeRate ?? 1.0;
  return base * Math.pow(ENHANCE_RATE_MULTIPLIER, level);
}

export function calcSpawnRate(elementId: string, level: number): number {
  const el = elementsData.find((e) => e.id === elementId);
  const baseSpawnSpeed = el?.baseSpawnSpeed ?? 5.0;
  return (1 / baseSpawnSpeed) * Math.pow(SPAWN_ENHANCE_MULTIPLIER, level);
}

export function isElementUnlocked(elementId: string, elements: Record<string, number>): boolean {
  const el = elementsData.find((e) => e.id === elementId);
  if (!el) return false;
  if (el.unlockCondition === 'initial') return true;
  if (el.unlockCondition.startsWith('recipe:')) {
    const ids = el.unlockCondition.replace('recipe:', '').split('+');
    return ids.every((id) => (elements[id] ?? 0) > 0);
  }
  return false;
}

export interface FuseResult {
  success: boolean;
  resultId?: string;
  goldGained?: number;
  error?: 'no_recipe' | 'insufficient_elements';
}

export interface EnhanceResult {
  success: boolean;
  newLevel?: number;
  cost?: number;
  error?: 'max_level' | 'insufficient_gold';
}

export interface OfflineReward {
  elements: Record<string, number>;
  gold: number;
  offlineSec: number;
}

export interface PrestigeResult {
  success: boolean;
  newPrestigeCount?: number;
  goldMultiplier?: number;
  spawnMultiplier?: number;
  title?: string;
  error?: 'not_enough_elements';
}

export interface AchievementStats {
  fuseCount: number;
  enhanceCount: number;
  offlineClaimCount: number;
  prestigeCount: number;
  tiersUnlocked: number[];
}

// 프레스티지 테이블에서 현재 횟수에 맞는 항목 조회
function getPrestigeEntry(count: number) {
  const table = prestigeTableData as Array<{
    count: number;
    goldMultiplier: number;
    spawnMultiplier: number;
    starterGold: number;
    title: string;
    titleEmoji: string;
    titleColor: string;
  }>;
  const entry = table.find((e) => e.count === count);
  // count가 테이블 최댓값을 초과하면 마지막 항목 사용
  if (!entry) return table[table.length - 1];
  return entry;
}

interface AchievementCondition {
  type: string;
  value: number;
  target?: string;
  tier?: number;
}

interface AchievementReward {
  gold?: number;
  boostType?: string;
  boostDurationMin?: number;
  bonusScrolls?: number;
  permanentGoldMultiplier?: number;
  permanentSpawnMultiplier?: number;
  title?: string;
  specialBackground?: string;
}

interface Achievement {
  id: string;
  name: string;
  description: string;
  category: string;
  condition: AchievementCondition;
  reward: AchievementReward;
  isHidden: boolean;
}

const ACHIEVEMENTS = achievementsData as Achievement[];

function checkAchievementCondition(
  condition: AchievementCondition,
  state: {
    elements: Record<string, number>;
    gold: number;
    elementLevels: Record<string, number>;
    achievementStats: AchievementStats;
  }
): boolean {
  const { elements, gold, elementLevels, achievementStats } = state;

  switch (condition.type) {
    case 'fuse_count':
      return achievementStats.fuseCount >= condition.value;

    case 'element_count':
      return (elements[condition.target ?? ''] ?? 0) >= condition.value;

    case 'tier_unlock':
      return achievementStats.tiersUnlocked.includes(condition.value);

    case 'enhance_count':
      return achievementStats.enhanceCount >= condition.value;

    case 'offline_claim':
      return achievementStats.offlineClaimCount >= condition.value;

    case 'element_variety':
      return Object.values(elements).filter((c) => c > 0).length >= condition.value;

    case 'gold_amount':
      return gold >= condition.value;

    case 'max_enhance_count': {
      const maxLevelCount = Object.values(elementLevels).filter(
        (lv) => lv >= ENHANCE_MAX_LEVEL
      ).length;
      return maxLevelCount >= condition.value;
    }

    case 'total_elements':
      return Object.values(elements).reduce((sum, c) => sum + c, 0) >= condition.value;

    case 'single_element_count':
      return Math.max(0, ...Object.values(elements)) >= condition.value;

    case 'tier_variety': {
      const targetTier = condition.tier ?? 0;
      const tierElementIds = (elementsData as Array<{ id: string; tier: number }>)
        .filter((e) => e.tier === targetTier)
        .map((e) => e.id);
      const discoveredCount = tierElementIds.filter((id) => (elements[id] ?? 0) > 0).length;
      return discoveredCount >= condition.value;
    }

    case 'all_enhance_min_level': {
      const discoveredIds = Object.entries(elements)
        .filter(([, count]) => count > 0)
        .map(([id]) => id);
      if (discoveredIds.length === 0) return false;
      return discoveredIds.every((id) => (elementLevels[id] ?? 0) >= condition.value);
    }

    case 'element_unlock':
      return (elements[condition.target ?? ''] ?? 0) > 0;

    case 'prestige_count':
      return achievementStats.prestigeCount >= condition.value;

    default:
      return false;
  }
}

interface AchievementUnlockResult {
  newlyUnlocked: Achievement[];
  goldBonus: number;
  permanentGoldMultiplierBonus: number;
  permanentSpawnMultiplierBonus: number;
}

function collectNewAchievements(
  state: {
    elements: Record<string, number>;
    gold: number;
    elementLevels: Record<string, number>;
    achievementStats: AchievementStats;
    unlockedAchievements: string[];
  }
): AchievementUnlockResult {
  const newlyUnlocked: Achievement[] = [];
  let goldBonus = 0;
  let permanentGoldMultiplierBonus = 0;
  let permanentSpawnMultiplierBonus = 0;

  for (const ach of ACHIEVEMENTS) {
    if (state.unlockedAchievements.includes(ach.id)) continue;
    if (checkAchievementCondition(ach.condition, state)) {
      newlyUnlocked.push(ach);
      goldBonus += ach.reward.gold ?? 0;
      permanentGoldMultiplierBonus += ach.reward.permanentGoldMultiplier ?? 0;
      permanentSpawnMultiplierBonus += ach.reward.permanentSpawnMultiplier ?? 0;
    }
  }

  return { newlyUnlocked, goldBonus, permanentGoldMultiplierBonus, permanentSpawnMultiplierBonus };
}

interface GameState {
  activeTab: TabName;
  setActiveTab: (tab: TabName) => void;

  // 원소 인벤토리
  elements: Record<string, number>;
  gold: number;
  addGold: (amount: number) => void;
  addElement: (elementId: string, count?: number) => void;

  // 원소 강화 레벨 (elementId -> level 0~5)
  elementLevels: Record<string, number>;
  enhance: (elementId: string) => EnhanceResult;

  // 합성 액션
  fuse: (slot1Id: string, slot2Id: string) => FuseResult;

  // 방치형 시스템
  lastTickAt: number;
  spawnAccumulators: Record<string, number>;
  pendingOfflineReward: OfflineReward | null;
  tickIdle: (deltaSec: number) => void;
  initOffline: () => void;
  claimOfflineReward: () => void;

  // 상점 버프 시스템 (boostId -> 만료 시각 ms)
  activeBoosts: Record<string, number>;
  activateBoost: (boostId: string, durationSec: number) => void;

  // 프레스티지 시스템
  prestigeCount: number;
  prestigeGoldMultiplier: number;
  prestigeSpawnMultiplier: number;
  prestigeTitle: string;
  prestige: () => PrestigeResult;

  // 업적 시스템
  unlockedAchievements: string[];
  permanentGoldMultiplier: number;
  permanentSpawnMultiplier: number;
  achievementStats: AchievementStats;

  // 튜토리얼 상태
  tutorialStep: number;       // 0~5 (0=미시작, 5=완료)
  tutorialCompleted: boolean; // true면 튜토리얼 UI 완전히 숨김
  advanceTutorial: () => void;
  skipTutorial: () => void;

  // 설정
  language: Language;
  bgmEnabled: boolean;
  setLanguage: (lang: Language) => void;
  setBgmEnabled: (enabled: boolean) => void;
  resetGame: () => void;
}

type PersistedState = Pick<
  GameState,
  | 'activeTab'
  | 'elements'
  | 'gold'
  | 'elementLevels'
  | 'lastTickAt'
  | 'activeBoosts'
  | 'spawnAccumulators'
  | 'language'
  | 'bgmEnabled'
  | 'prestigeCount'
  | 'prestigeGoldMultiplier'
  | 'prestigeSpawnMultiplier'
  | 'prestigeTitle'
  | 'unlockedAchievements'
  | 'permanentGoldMultiplier'
  | 'permanentSpawnMultiplier'
  | 'achievementStats'
  | 'tutorialStep'
  | 'tutorialCompleted'
>;

export const useGameStore = create<GameState>()(
  persist(
    (set, get) => ({
      activeTab: 'elements',
      setActiveTab: (tab) => set({ activeTab: tab }),

      elements: { ...INITIAL_ELEMENTS },
      gold: 0,
      activeBoosts: {},

      addGold: (amount) => set((state) => ({ gold: state.gold + amount })),

      addElement: (elementId, count = 1) =>
        set((state) => ({
          elements: {
            ...state.elements,
            [elementId]: (state.elements[elementId] ?? 0) + count,
          },
        })),

      elementLevels: {},

      enhance: (elementId) => {
        const { elementLevels, gold } = get();
        const currentLevel = elementLevels[elementId] ?? 0;

        if (currentLevel >= ENHANCE_MAX_LEVEL) {
          return { success: false, error: 'max_level' };
        }

        const cost = calcEnhanceCost(currentLevel);
        if (gold < cost) {
          return { success: false, error: 'insufficient_gold' };
        }

        const newLevel = currentLevel + 1;
        set((state) => {
          const newElementLevels = {
            ...state.elementLevels,
            [elementId]: newLevel,
          };
          const newStats: AchievementStats = {
            ...state.achievementStats,
            enhanceCount: state.achievementStats.enhanceCount + 1,
          };
          const checkState = {
            elements: state.elements,
            gold: state.gold - cost,
            elementLevels: newElementLevels,
            achievementStats: newStats,
            unlockedAchievements: state.unlockedAchievements,
          };
          const { newlyUnlocked, goldBonus, permanentGoldMultiplierBonus, permanentSpawnMultiplierBonus } =
            collectNewAchievements(checkState);
          for (const ach of newlyUnlocked) {
            window.dispatchEvent(new CustomEvent('newAchievement', { detail: ach }));
          }
          return {
            gold: state.gold - cost + goldBonus,
            elementLevels: newElementLevels,
            achievementStats: newStats,
            unlockedAchievements: [
              ...state.unlockedAchievements,
              ...newlyUnlocked.map((a) => a.id),
            ],
            permanentGoldMultiplier: state.permanentGoldMultiplier + permanentGoldMultiplierBonus,
            permanentSpawnMultiplier:
              state.permanentSpawnMultiplier + permanentSpawnMultiplierBonus,
          };
        });

        return { success: true, newLevel, cost };
      },

      fuse: (slot1Id, slot2Id) => {
        const { elements } = get();

        const recipe = recipesData.find(
          (r) =>
            (r.ingredients[0] === slot1Id && r.ingredients[1] === slot2Id) ||
            (r.ingredients[0] === slot2Id && r.ingredients[1] === slot1Id)
        );

        if (!recipe) return { success: false, error: 'no_recipe' };

        const slot1Count = elements[slot1Id] ?? 0;
        const slot2Count = elements[slot2Id] ?? 0;
        const sameElement = slot1Id === slot2Id;

        if (sameElement && slot1Count < 2) return { success: false, error: 'insufficient_elements' };
        if (!sameElement && (slot1Count < 1 || slot2Count < 1))
          return { success: false, error: 'insufficient_elements' };

        const goldGained = recipe.tier * 10;
        const resultTier =
          (elementsData as Array<{ id: string; tier: number }>).find(
            (e) => e.id === recipe.result
          )?.tier ?? 0;

        set((state) => {
          const next = { ...state.elements };
          next[slot1Id] = (next[slot1Id] ?? 0) - 1;
          if (!sameElement) {
            next[slot2Id] = (next[slot2Id] ?? 0) - 1;
          }
          next[recipe.result] = (next[recipe.result] ?? 0) + 1;

          const newTiersUnlocked = state.achievementStats.tiersUnlocked.includes(resultTier)
            ? state.achievementStats.tiersUnlocked
            : [...state.achievementStats.tiersUnlocked, resultTier];

          const newStats: AchievementStats = {
            ...state.achievementStats,
            fuseCount: state.achievementStats.fuseCount + 1,
            tiersUnlocked: newTiersUnlocked,
          };

          const checkState = {
            elements: next,
            gold: state.gold + goldGained,
            elementLevels: state.elementLevels,
            achievementStats: newStats,
            unlockedAchievements: state.unlockedAchievements,
          };
          const { newlyUnlocked, goldBonus, permanentGoldMultiplierBonus, permanentSpawnMultiplierBonus } =
            collectNewAchievements(checkState);
          for (const ach of newlyUnlocked) {
            window.dispatchEvent(new CustomEvent('newAchievement', { detail: ach }));
          }

          return {
            elements: next,
            gold: state.gold + goldGained + goldBonus,
            achievementStats: newStats,
            unlockedAchievements: [
              ...state.unlockedAchievements,
              ...newlyUnlocked.map((a) => a.id),
            ],
            permanentGoldMultiplier: state.permanentGoldMultiplier + permanentGoldMultiplierBonus,
            permanentSpawnMultiplier:
              state.permanentSpawnMultiplier + permanentSpawnMultiplierBonus,
          };
        });

        return { success: true, resultId: recipe.result, goldGained };
      },

      // 설정
      language: 'ko',
      bgmEnabled: false,
      setLanguage: (lang) => set({ language: lang }),
      setBgmEnabled: (enabled) => set({ bgmEnabled: enabled }),
      resetGame: () => {
        flushGameState();
        localStorage.removeItem('archetype-game-state');
        window.location.reload();
      },

      // 상점 버프 시스템
      activateBoost: (boostId, durationSec) => {
        set((state) => ({
          activeBoosts: {
            ...state.activeBoosts,
            [boostId]: Date.now() + durationSec * 1000,
          },
        }));
      },

      // 프레스티지 시스템
      prestigeCount: 0,
      prestigeGoldMultiplier: 1.0,
      prestigeSpawnMultiplier: 1.0,
      prestigeTitle: '',

      prestige: () => {
        const state = get();
        const creationCount = state.elements['creation'] ?? 0;
        const spiritCount = state.elements['spirit'] ?? 0;

        if (creationCount < 1 || spiritCount < 1) {
          return { success: false, error: 'not_enough_elements' };
        }

        const newPrestigeCount = state.prestigeCount + 1;
        const entry = getPrestigeEntry(newPrestigeCount);

        const newStats: AchievementStats = {
          ...state.achievementStats,
          prestigeCount: newPrestigeCount,
        };

        const checkState = {
          elements: { ...INITIAL_ELEMENTS },
          gold: entry.starterGold,
          elementLevels: {},
          achievementStats: newStats,
          unlockedAchievements: state.unlockedAchievements,
        };
        const { newlyUnlocked, goldBonus, permanentGoldMultiplierBonus, permanentSpawnMultiplierBonus } =
          collectNewAchievements(checkState);
        for (const ach of newlyUnlocked) {
          window.dispatchEvent(new CustomEvent('newAchievement', { detail: ach }));
        }

        set({
          // 초기화 항목
          elements: { ...INITIAL_ELEMENTS },
          elementLevels: {},
          activeBoosts: {},
          // 골드는 스타터 골드 + 업적 보너스
          gold: entry.starterGold + goldBonus,
          // 유지/업데이트 항목
          prestigeCount: newPrestigeCount,
          prestigeGoldMultiplier: entry.goldMultiplier,
          prestigeSpawnMultiplier: entry.spawnMultiplier,
          prestigeTitle: `${entry.titleEmoji} ${entry.title}`,
          achievementStats: newStats,
          unlockedAchievements: [
            ...state.unlockedAchievements,
            ...newlyUnlocked.map((a) => a.id),
          ],
          permanentGoldMultiplier:
            state.permanentGoldMultiplier + permanentGoldMultiplierBonus,
          permanentSpawnMultiplier:
            state.permanentSpawnMultiplier + permanentSpawnMultiplierBonus,
          spawnAccumulators: {},
          lastTickAt: Date.now(),
        });

        return {
          success: true,
          newPrestigeCount,
          goldMultiplier: entry.goldMultiplier,
          spawnMultiplier: entry.spawnMultiplier,
          title: `${entry.titleEmoji} ${entry.title}`,
        };
      },

      // 업적 시스템
      unlockedAchievements: [],
      permanentGoldMultiplier: 1.0,
      permanentSpawnMultiplier: 1.0,
      achievementStats: {
        fuseCount: 0,
        enhanceCount: 0,
        offlineClaimCount: 0,
        prestigeCount: 0,
        tiersUnlocked: [],
      },

      // 튜토리얼 상태
      tutorialStep: 0,
      tutorialCompleted: false,
      advanceTutorial: () =>
        set((state) => {
          const nextStep = state.tutorialStep + 1;
          return {
            tutorialStep: nextStep,
            tutorialCompleted: nextStep >= 5,
          };
        }),
      skipTutorial: () => set({ tutorialStep: 5, tutorialCompleted: true }),

      // 방치형 시스템
      lastTickAt: Date.now(),
      spawnAccumulators: {},
      pendingOfflineReward: null,

      tickIdle: (deltaSec) => {
        const {
          elements,
          elementLevels,
          spawnAccumulators,
          activeBoosts,
          prestigeGoldMultiplier,
          prestigeSpawnMultiplier,
          permanentGoldMultiplier,
          permanentSpawnMultiplier,
        } = get();
        const newAccumulators = { ...spawnAccumulators };
        const spawnedElements: Record<string, number> = {};
        let goldGained = 0;
        const now = Date.now();

        for (const el of elementsData) {
          if (!isElementUnlocked(el.id, elements)) continue;
          const level = elementLevels[el.id] ?? 0;

          // 버프 배수 계산 (해당 원소 강화석이 활성 중이면 2배)
          const boostId = el.id + '_boost';
          const boostMultiplier = (activeBoosts[boostId] ?? 0) > now ? 2.0 : 1.0;

          // 원소 자동 생성 (프레스티지 + 영구 스폰 배율 적용)
          const rate =
            calcSpawnRate(el.id, level) *
            boostMultiplier *
            prestigeSpawnMultiplier *
            permanentSpawnMultiplier;
          const prev = newAccumulators[el.id] ?? 0;
          const next = prev + rate * deltaSec;
          const spawned = Math.floor(next);
          newAccumulators[el.id] = next - spawned;
          if (spawned > 0) spawnedElements[el.id] = spawned;

          // 골드 자동 수입 (프레스티지 + 영구 골드 배율 적용)
          goldGained +=
            calcEffectiveIdleRate(el.id, level) *
            deltaSec *
            prestigeGoldMultiplier *
            permanentGoldMultiplier;
        }

        set((state) => {
          const newElements = { ...state.elements };
          for (const [id, count] of Object.entries(spawnedElements)) {
            newElements[id] = (newElements[id] ?? 0) + count;
          }
          const goldToAdd = Math.floor(goldGained);

          // tickIdle에서도 gold_amount, element_count 등 업적 체크
          const checkState = {
            elements: newElements,
            gold: state.gold + goldToAdd,
            elementLevels: state.elementLevels,
            achievementStats: state.achievementStats,
            unlockedAchievements: state.unlockedAchievements,
          };
          const {
            newlyUnlocked,
            goldBonus,
            permanentGoldMultiplierBonus,
            permanentSpawnMultiplierBonus,
          } = collectNewAchievements(checkState);
          for (const ach of newlyUnlocked) {
            window.dispatchEvent(new CustomEvent('newAchievement', { detail: ach }));
          }

          return {
            elements: newElements,
            gold: state.gold + goldToAdd + goldBonus,
            spawnAccumulators: newAccumulators,
            lastTickAt: Date.now(),
            unlockedAchievements: [
              ...state.unlockedAchievements,
              ...newlyUnlocked.map((a) => a.id),
            ],
            permanentGoldMultiplier:
              state.permanentGoldMultiplier + permanentGoldMultiplierBonus,
            permanentSpawnMultiplier:
              state.permanentSpawnMultiplier + permanentSpawnMultiplierBonus,
          };
        });
      },

      initOffline: () => {
        const {
          lastTickAt,
          elements,
          elementLevels,
          prestigeSpawnMultiplier,
          permanentSpawnMultiplier,
          prestigeGoldMultiplier,
          permanentGoldMultiplier,
        } = get();
        const now = Date.now();
        const offlineSec = Math.min((now - lastTickAt) / 1000, MAX_OFFLINE_SECONDS);

        if (offlineSec < 10) {
          set({ lastTickAt: now });
          return;
        }

        const rewardElements: Record<string, number> = {};
        let rewardGold = 0;

        for (const el of elementsData) {
          if (!isElementUnlocked(el.id, elements)) continue;
          const level = elementLevels[el.id] ?? 0;

          const spawned = Math.floor(
            calcSpawnRate(el.id, level) *
              prestigeSpawnMultiplier *
              permanentSpawnMultiplier *
              offlineSec
          );
          if (spawned > 0) rewardElements[el.id] = spawned;

          rewardGold +=
            calcEffectiveIdleRate(el.id, level) *
            offlineSec *
            prestigeGoldMultiplier *
            permanentGoldMultiplier;
        }

        set({
          lastTickAt: now,
          pendingOfflineReward: {
            elements: rewardElements,
            gold: Math.floor(rewardGold),
            offlineSec: Math.floor(offlineSec),
          },
        });
      },

      claimOfflineReward: () => {
        const { pendingOfflineReward } = get();
        if (!pendingOfflineReward) return;

        set((state) => {
          const newElements = { ...state.elements };
          for (const [id, count] of Object.entries(pendingOfflineReward.elements)) {
            newElements[id] = (newElements[id] ?? 0) + count;
          }
          const newStats: AchievementStats = {
            ...state.achievementStats,
            offlineClaimCount: state.achievementStats.offlineClaimCount + 1,
          };
          const checkState = {
            elements: newElements,
            gold: state.gold + pendingOfflineReward.gold,
            elementLevels: state.elementLevels,
            achievementStats: newStats,
            unlockedAchievements: state.unlockedAchievements,
          };
          const {
            newlyUnlocked,
            goldBonus,
            permanentGoldMultiplierBonus,
            permanentSpawnMultiplierBonus,
          } = collectNewAchievements(checkState);
          for (const ach of newlyUnlocked) {
            window.dispatchEvent(new CustomEvent('newAchievement', { detail: ach }));
          }

          return {
            elements: newElements,
            gold: state.gold + pendingOfflineReward.gold + goldBonus,
            pendingOfflineReward: null,
            achievementStats: newStats,
            unlockedAchievements: [
              ...state.unlockedAchievements,
              ...newlyUnlocked.map((a) => a.id),
            ],
            permanentGoldMultiplier:
              state.permanentGoldMultiplier + permanentGoldMultiplierBonus,
            permanentSpawnMultiplier:
              state.permanentSpawnMultiplier + permanentSpawnMultiplierBonus,
          };
        });
      },
    }),
    {
      name: 'archetype-game-state',
      storage: throttledStorage,
      partialize: (state): PersistedState => ({
        activeTab: state.activeTab,
        elements: state.elements,
        gold: state.gold,
        elementLevels: state.elementLevels,
        lastTickAt: state.lastTickAt,
        activeBoosts: state.activeBoosts,
        spawnAccumulators: state.spawnAccumulators,
        language: state.language,
        bgmEnabled: state.bgmEnabled,
        prestigeCount: state.prestigeCount,
        prestigeGoldMultiplier: state.prestigeGoldMultiplier,
        prestigeSpawnMultiplier: state.prestigeSpawnMultiplier,
        prestigeTitle: state.prestigeTitle,
        unlockedAchievements: state.unlockedAchievements,
        permanentGoldMultiplier: state.permanentGoldMultiplier,
        permanentSpawnMultiplier: state.permanentSpawnMultiplier,
        achievementStats: state.achievementStats,
        tutorialStep: state.tutorialStep,
        tutorialCompleted: state.tutorialCompleted,
      }),
      onRehydrateStorage: () => (state) => {
        // 앱 재시작 시 localStorage에서 복원된 만료 부스트를 자동 정리
        if (!state) return;
        const now = Date.now();
        const validBoosts: Record<string, number> = {};
        for (const [key, expiresAt] of Object.entries(state.activeBoosts)) {
          if (expiresAt > now) validBoosts[key] = expiresAt;
        }
        state.activeBoosts = validBoosts;

        // achievementStats 누락 필드 기본값 보정 (구버전 저장 데이터 호환)
        if (!state.achievementStats) {
          state.achievementStats = {
            fuseCount: 0,
            enhanceCount: 0,
            offlineClaimCount: 0,
            prestigeCount: 0,
            tiersUnlocked: [],
          };
        }
        if (!Array.isArray(state.achievementStats.tiersUnlocked)) {
          state.achievementStats.tiersUnlocked = [];
        }
        if (!state.unlockedAchievements) state.unlockedAchievements = [];
        if (!state.permanentGoldMultiplier) state.permanentGoldMultiplier = 1.0;
        if (!state.permanentSpawnMultiplier) state.permanentSpawnMultiplier = 1.0;
        if (!state.prestigeCount) state.prestigeCount = 0;
        if (!state.prestigeGoldMultiplier) state.prestigeGoldMultiplier = 1.0;
        if (!state.prestigeSpawnMultiplier) state.prestigeSpawnMultiplier = 1.0;
        if (!state.prestigeTitle) state.prestigeTitle = '';
      },
    }
  )
);
