// ─── Game Store (Coordinator) ─────────────────────────────────────────────────
// Manages: day, hour, incursionStrength, containmentWards
// Orchestrates tick across all stores via a read → compute → write pipeline.

import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { TICK_MS, HOURS_PER_TICK, MAX_DAY, GUARDIANS, getStudySpeedMultiplier } from '../constants';
import { hasSpecial, SPECIAL_EFFECTS } from '../effects/special-effects';
import { computeEquipmentEffects } from '../effects';
import type { ComputedEffects } from '../effects/upgrade-effects.types';

import { computeDisciplineEffects } from '../effects/discipline-effects';
import {
  computeMaxMana,
  computeRegen,
  getFloorElement,
  getFloorMaxHP,
  getMeditationBonus,
  getIncursionStrength,
  calcInsight,
  calcDamage,
  deductSpellCost,
  canAffordSpellCost,
} from '../utils';
import { useUIStore } from './uiStore';
import { usePrestigeStore } from './prestigeStore';
import { useManaStore } from './manaStore';
import { useCombatStore, makeInitialSpells } from './combatStore';
import { useAttunementStore } from './attunementStore';
import { useCraftingStore } from './craftingStore';
import { useDisciplineStore } from './discipline-slice';
import { ATTUNEMENTS_DEF, getAttunementConversionRate } from '../data/attunements';
import { createResetGame, createGatherMana } from './gameActions';
import { createSafeStorage } from '../utils/safe-persist';
import { createStartNewLoop } from './gameLoopActions';
import { buildTickContext, applyTickWrites } from './tick-pipeline';
import type { TickContext, TickWrites } from './tick-pipeline';

export interface GameCoordinatorState {
  day: number;
  hour: number;
  incursionStrength: number;
  containmentWards: number;
  initialized: boolean;
}

export interface GameCoordinatorStore extends GameCoordinatorState {
  tick: () => void;
  resetGame: () => void;
  togglePause: () => void;
  startNewLoop: () => void;
  initGame: () => void;
  gatherMana: () => void;
}

const initialState: GameCoordinatorState = {
  day: 1,
  hour: 0,
  incursionStrength: 0,
  containmentWards: 0,
  initialized: false,
};

export const useGameStore = create<GameCoordinatorStore>()(
  persist(
    (set, get) => ({
      ...initialState,

      initGame: () => {
        set({ initialized: true });
      },

      tick: () => {
        try {
          // ── Phase 1: Read — snapshot all store states once ──────────────────
          const ctx = buildTickContext({
            game: get(),
            ui: useUIStore.getState(),
            prestige: usePrestigeStore.getState(),
            mana: useManaStore.getState(),
            combat: useCombatStore.getState(),
            crafting: useCraftingStore.getState(),
            attunement: useAttunementStore.getState(),
            discipline: useDisciplineStore.getState(),
          });

          if (ctx.ui.gameOver || ctx.ui.paused) return;

        // ── Phase 2: Compute — derive all updates ───────────────────────────
        const writes: TickWrites = { logs: [] };
        const addLog = (msg: string) => writes.logs.push(msg);

        // Compute equipment and discipline effects
        const equipmentEffects = computeEquipmentEffects(
          ctx.crafting.equipmentInstances || {},
          ctx.crafting.equippedInstances || {}
        );
        const disciplineEffects = computeDisciplineEffects();
        const allSpecials = new Set<string>([
          ...equipmentEffects.specials,
          ...disciplineEffects.specials,
        ]);
        const effects = { specials: allSpecials } as ComputedEffects;

        const maxMana = computeMaxMana(
          { skills: {}, prestigeUpgrades: ctx.prestige.prestigeUpgrades, skillUpgrades: {}, skillTiers: {} },
          undefined,
          disciplineEffects,
        );
        const baseRegen = computeRegen(
          { skills: {}, prestigeUpgrades: ctx.prestige.prestigeUpgrades, skillUpgrades: {}, skillTiers: {}, attunements: {} },
          undefined,
          disciplineEffects,
        );

        // Time progression
        let hour = ctx.game.hour + HOURS_PER_TICK;
        let day = ctx.game.day;
        if (hour >= 24) {
          hour -= 24;
          day += 1;
        }

        // Check for loop end
        if (day > MAX_DAY) {
          const insightGained = calcInsight({
            maxFloorReached: ctx.combat.maxFloorReached,
            totalManaGathered: ctx.mana.totalManaGathered,
            signedPacts: ctx.prestige.signedPacts,
            prestigeUpgrades: ctx.prestige.prestigeUpgrades,
            skills: {},
          }, disciplineEffects);

          addLog(`⏰ The loop ends. Gained ${insightGained} Insight.`);
          writes.ui = { ...(writes.ui || {}), gameOver: true, victory: false };
          writes.prestige = { ...(writes.prestige || {}), loopInsight: insightGained };
          writes.game = { day, hour };
          applyTickWrites(writes, {
            setGame: set,
            setUI: (w) => useUIStore.setState(w),
            setPrestige: (w) => usePrestigeStore.setState(w),
            setMana: (w) => useManaStore.setState(w),
            setCombat: (w) => useCombatStore.setState(w),
            setCrafting: (w) => useCraftingStore.setState(w),
            setAttunement: (w) => useAttunementStore.setState(w),
            setDiscipline: (w) => useDisciplineStore.setState(w),
            addLogs: (msgs) => msgs.forEach((m) => useUIStore.getState().addLog(m)),
          });
          return;
        }

        // Check for victory
        if (ctx.combat.maxFloorReached >= 100 && ctx.prestige.signedPacts.includes(100)) {
          const insightGained = calcInsight({
            maxFloorReached: ctx.combat.maxFloorReached,
            totalManaGathered: ctx.mana.totalManaGathered,
            signedPacts: ctx.prestige.signedPacts,
            prestigeUpgrades: ctx.prestige.prestigeUpgrades,
            skills: {},
          }, disciplineEffects) * 3;

          addLog(`🏆 VICTORY! The Awakened One falls! Gained ${insightGained} Insight!`);
          writes.ui = { ...(writes.ui || {}), gameOver: true, victory: true };
          writes.prestige = { ...(writes.prestige || {}), loopInsight: insightGained };
          applyTickWrites(writes, {
            setGame: set,
            setUI: (w) => useUIStore.setState(w),
            setPrestige: (w) => usePrestigeStore.setState(w),
            setMana: (w) => useManaStore.setState(w),
            setCombat: (w) => useCombatStore.setState(w),
            setCrafting: (w) => useCraftingStore.setState(w),
            setAttunement: (w) => useAttunementStore.setState(w),
            setDiscipline: (w) => useDisciplineStore.setState(w),
            addLogs: (msgs) => msgs.forEach((m) => useUIStore.getState().addLog(m)),
          });
          return;
        }

        // Incursion
        const incursionStrength = getIncursionStrength(day, hour);

        // Meditation bonus tracking
        let meditateTicks = ctx.mana.meditateTicks;
        let meditationMultiplier = 1;

        if (ctx.combat.currentAction === 'meditate') {
          meditateTicks++;
          meditationMultiplier = getMeditationBonus(meditateTicks, {}, 1);
        } else {
          meditateTicks = 0;
        }

        // Calculate total attunement conversion per tick
        let totalConversionPerTick = 0;
        Object.entries(ctx.attunement.attunements).forEach(([id, state]) => {
          if (!state.active) return;
          const def = ATTUNEMENTS_DEF[id];
          if (!def || def.conversionRate <= 0 || !def.primaryManaType) return;
          const scaledRate = getAttunementConversionRate(id, state.level || 1);
          totalConversionPerTick += scaledRate * HOURS_PER_TICK;
        });

        // Calculate effective regen
        const effectiveRegen = Math.max(0, baseRegen * (1 - incursionStrength) * meditationMultiplier - totalConversionPerTick);

        // Mana regeneration
        let rawMana = Math.min(ctx.mana.rawMana + effectiveRegen * HOURS_PER_TICK, maxMana);
        let elements = { ...ctx.mana.elements };

        // Apply attunement conversion
        Object.entries(ctx.attunement.attunements).forEach(([id, state]) => {
          if (!state.active) return;
          const def = ATTUNEMENTS_DEF[id];
          if (!def || def.conversionRate <= 0 || !def.primaryManaType) return;
          const scaledRate = getAttunementConversionRate(id, state.level || 1);
          const conversionThisTick = scaledRate * HOURS_PER_TICK;
          if (elements[def.primaryManaType]) {
            elements[def.primaryManaType].current = Math.min(
              elements[def.primaryManaType].max,
              elements[def.primaryManaType].current + conversionThisTick
            );
          }
        });
        let totalManaGathered = ctx.mana.totalManaGathered;

        // Convert action — delegate to manaStore
        if (ctx.combat.currentAction === 'convert') {
          const convertResult = useManaStore.getState().processConvertAction(rawMana);
          if (convertResult) {
            rawMana = convertResult.rawMana;
            elements = convertResult.elements;
          }
        }

        // Pact ritual progress
        if (ctx.prestige.pactRitualFloor !== null) {
          const guardian = GUARDIANS[ctx.prestige.pactRitualFloor];
          if (guardian) {
            const pactAffinityBonus = 1 - (ctx.prestige.prestigeUpgrades.pactAffinity || 0) * 0.1;
            const requiredTime = guardian.pactTime * pactAffinityBonus;
            const newProgress = ctx.prestige.pactRitualProgress + HOURS_PER_TICK;

            if (newProgress >= requiredTime) {
              addLog(`📜 Pact signed with ${guardian.name}! You have gained their boons.`);
              writes.prestige = {
                ...(writes.prestige || {}),
                signedPacts: [...ctx.prestige.signedPacts, ctx.prestige.pactRitualFloor],
                defeatedGuardians: ctx.prestige.defeatedGuardians.filter(f => f !== ctx.prestige.pactRitualFloor),
                pactRitualFloor: null,
                pactRitualProgress: 0,
              };
            } else {
              writes.prestige = {
                ...(writes.prestige || {}),
                pactRitualProgress: newProgress,
              };
            }
          }
        }

        // Discipline tick — process active disciplines (XP accrual + mana drain)
        const disciplineResult = useDisciplineStore.getState().processTick({
          rawMana,
          elements,
        });
        rawMana = disciplineResult.rawMana;
        elements = disciplineResult.elements;

        // Combat — delegate to combatStore
        if (ctx.combat.currentAction === 'climb') {
          const combatResult = useCombatStore.getState().processCombatTick(
            rawMana,
            elements,
            maxMana,
            1,
            (floor, wasGuardian) => {
              if (wasGuardian) {
                addLog(`⚔️ ${GUARDIANS[floor]?.name || 'Guardian'} defeated! Visit the Grimoire to sign a pact.`);
              } else if (floor % 5 === 0) {
                addLog(`🏰 Floor ${floor} cleared!`);
              }
            },
            (damage) => {
              let dmg = damage;
              if (hasSpecial(effects, SPECIAL_EFFECTS.EXECUTIONER) && ctx.combat.floorHP / ctx.combat.floorMaxHP < 0.25) {
                dmg *= 2;
              }
              if (hasSpecial(effects, SPECIAL_EFFECTS.BERSERKER) && rawMana < maxMana * 0.5) {
                dmg *= 1.5;
              }
              return { rawMana, elements, modifiedDamage: dmg };
            },
            ctx.prestige.signedPacts,
          );

          rawMana = combatResult.rawMana;
          elements = combatResult.elements;
          totalManaGathered += combatResult.totalManaGathered || 0;

          if (combatResult.logMessages) {
            combatResult.logMessages.forEach(msg => addLog(msg));
          }

          writes.combat = {
            ...(writes.combat || {}),
            currentFloor: combatResult.currentFloor,
            floorHP: combatResult.floorHP,
            floorMaxHP: combatResult.floorMaxHP,
            maxFloorReached: combatResult.maxFloorReached,
            castProgress: combatResult.castProgress,
            equipmentSpellStates: combatResult.equipmentSpellStates,
          };
        }

        // ── Phase 3: Write — batch all state updates ─────────────────────────
        writes.game = { day, hour, incursionStrength };
        writes.mana = {
          rawMana,
          meditateTicks,
          totalManaGathered,
          elements,
        };

        applyTickWrites(writes, {
          setGame: set,
          setUI: (w) => useUIStore.setState(w),
          setPrestige: (w) => usePrestigeStore.setState(w),
          setMana: (w) => useManaStore.setState(w),
          setCombat: (w) => useCombatStore.setState(w),
          setCrafting: (w) => useCraftingStore.setState(w),
          setAttunement: (w) => useAttunementStore.setState(w),
          setDiscipline: (w) => useDisciplineStore.setState(w),
          addLogs: (msgs) => msgs.forEach((m) => useUIStore.getState().addLog(m)),
        });
        } catch (error) {
          // Log error to UI store if available, otherwise console error
          try {
            useUIStore.getState().addLog(`⚠️ Tick error: ${error.message}`);
          } catch {
            console.error('Tick error:', error);
          }
        }
      },

      resetGame: createResetGame(set, initialState),
      togglePause: () => {
        useUIStore.getState().togglePause();
      },
      startNewLoop: createStartNewLoop(set),
      gatherMana: createGatherMana(),
    }),
    {
      storage: createSafeStorage(),
      name: 'mana-loop-game-storage',
      partialize: (state) => ({
        day: state.day,
        hour: state.hour,
        incursionStrength: state.incursionStrength,
        containmentWards: state.containmentWards,
      }),
    }
  )
);
