import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
import type { QuestArc, QuestNode } from "../types/quest";
import { CREW_RANKS } from "../types/quest";
import { QUEST_ARCS } from "../data/questData";

// ─── Types ────────────────────────────────────────────────────────────────────

export interface CrewRank {
  id: string;
  label: string;
  emoji: string;
  xpRequired: number;
  progressToNext: number; // 0–1 toward next rank
  next: { label: string; xpRequired: number } | null;
}

export interface QuestSummary {
  totalNodes: number;
  completedNodes: number;
  activeNodes: number;
  claimableNodes: number;
  lockedNodes: number;
  // totalXP removed — node definitions no longer carry an XP value.
  // Awarded XP only comes back from ClaimedRewardResponse at claim time.
  earnedXP: number; // accumulated from claim responses, stored in state
  arcsCompleted: number;
  totalArcs: number;
  earnedTitles: string[]; // accumulated from claim responses, stored in state
  crewRank: CrewRank;
}

// ─── Store ────────────────────────────────────────────────────────────────────

interface QuestStore {
  arcs: QuestArc[];
  activeArcId: string;
  // XP and titles are no longer derivable from node fields alone —
  // they come from ClaimedRewardResponse at claim time, so we track them here.
  earnedXP: number;
  earnedTitles: string[];

  setActiveArc: (arcId: string) => void;
  /**
   * Call this after a successful /journey/claim API response.
   * Pass the xp and titles returned by ClaimedRewardResponse so the store
   * stays in sync without needing to re-fetch the whole journey.
   */
  claimNode: (
    arcId: string,
    nodeId: string,
    xpAwarded?: number,
    titlesAwarded?: string[],
  ) => void;
  syncFromAPI: (arcs: QuestArc[]) => void;
}

export const useQuestStore = create<QuestStore>()(
  persist(
    (set) => ({
      arcs: QUEST_ARCS,
      activeArcId: QUEST_ARCS[0].id,
      earnedXP: 0,
      earnedTitles: [],

      setActiveArc: (arcId) => set({ activeArcId: arcId }),

      claimNode: (arcId, nodeId, xpAwarded = 0, titlesAwarded = []) =>
        set((state) => ({
          // Accumulate XP and titles from the claim response
          earnedXP: state.earnedXP + xpAwarded,
          earnedTitles: [...state.earnedTitles, ...titlesAwarded],

          arcs: state.arcs.map((arc) => {
            if (arc.id !== arcId) return arc;
            // node_id is the new primary key — replaces old n.id
            const nodeIdx = arc.nodes.findIndex((n) => n.node_id === nodeId);
            if (nodeIdx === -1) return arc;
            return {
              ...arc,
              nodes: arc.nodes.map((n, i) => {
                if (n.node_id === nodeId) return { ...n, status: "completed" };
                // Unlock the next locked node in sequence
                if (i === nodeIdx + 1 && n.status === "locked")
                  return { ...n, status: "active" };
                return n;
              }),
            };
          }),
        })),

      syncFromAPI: (arcs) => set({ arcs }),
    }),
    {
      name: "quest-store",
      storage: createJSONStorage(() => localStorage),
      partialize: (state) => ({
        arcs: state.arcs,
        activeArcId: state.activeArcId,
        earnedXP: state.earnedXP,
        earnedTitles: state.earnedTitles,
      }),
    },
  ),
);

// ─── Standalone helper functions ──────────────────────────────────────────────

export function getCrewRank(earnedXP: number): CrewRank {
  // Accepts earnedXP directly — no longer iterates nodes (reward.xp is gone)
  const ladder = [...CREW_RANKS];
  let idx = 0;
  for (let i = ladder.length - 1; i >= 0; i--) {
    if (earnedXP >= ladder[i].xpRequired) {
      idx = i;
      break;
    }
  }
  const current = ladder[idx];
  const nextRank = ladder[idx + 1] ?? null;
  return {
    ...current,
    progressToNext: nextRank
      ? Math.min(
          1,
          (earnedXP - current.xpRequired) /
            (nextRank.xpRequired - current.xpRequired),
        )
      : 1,
    next: nextRank
      ? { label: nextRank.label, xpRequired: nextRank.xpRequired }
      : null,
  };
}

export function getQuestSummary(
  arcs: QuestArc[],
  earnedXP: number,
  earnedTitles: string[],
): QuestSummary {
  const allNodes = arcs.flatMap((a) => a.nodes);
  return {
    totalNodes: allNodes.length,
    completedNodes: allNodes.filter((n) => n.status === "completed").length,
    activeNodes: allNodes.filter((n) => n.status === "active").length,
    claimableNodes: allNodes.filter((n) => n.status === "claimable").length,
    lockedNodes: allNodes.filter((n) => n.status === "locked").length,
    earnedXP,
    arcsCompleted: arcs.filter((a) =>
      a.nodes.every((n) => n.status === "completed"),
    ).length,
    totalArcs: arcs.length,
    earnedTitles,
    crewRank: getCrewRank(earnedXP),
  };
}

export function getClaimableCount(arcs: QuestArc[]): number {
  return arcs.flatMap((a) => a.nodes).filter((n) => n.status === "claimable")
    .length;
}

// node_id is the new primary key — replaces old n.id
export function getNode(
  arcs: QuestArc[],
  nodeId: string,
): QuestNode | undefined {
  return arcs.flatMap((a) => a.nodes).find((n) => n.node_id === nodeId);
}

export function getActiveArc(arcs: QuestArc[], activeArcId: string): QuestArc {
  return arcs.find((a) => a.id === activeArcId) ?? arcs[0];
}
