import { create } from 'zustand'
import { toast } from '../components/ToastContainer'

const MAX_STACK = 20

export interface UndoEntry {
  id: string
  /** Short description of what happened, e.g. "Discarded 12 photos". */
  label: string
  /** Function that reverses the action. May be async; errors should be
   *  surfaced via toast.error from inside the function. */
  undo: () => void | Promise<void>
}

interface UndoStore {
  stack: UndoEntry[]
  /** Push a new entry. Caps the stack at MAX_STACK by dropping the oldest. */
  push: (entry: Omit<UndoEntry, 'id'>) => void
  /** Pop the most recent entry. Returns null when the stack is empty. */
  pop: () => UndoEntry | null
  clear: () => void
}

export const useUndoStore = create<UndoStore>((set, get) => ({
  stack: [],

  push: (entry) => {
    const id = Date.now().toString() + Math.random().toString(36).slice(2, 6)
    set((s) => {
      const next = [...s.stack, { ...entry, id }]
      if (next.length > MAX_STACK) next.shift()
      return { stack: next }
    })
  },

  pop: () => {
    const stack = get().stack
    if (stack.length === 0) return null
    const last = stack[stack.length - 1]
    set({ stack: stack.slice(0, -1) })
    return last
  },

  clear: () => set({ stack: [] }),
}))

/**
 * Convenience: register an undoable action AND show the user a success
 * toast with an inline Undo button. The toast and Cmd+Z hotkey both pop
 * from the same stack so either path works.
 */
export function registerUndoable(label: string, undo: () => void | Promise<void>) {
  useUndoStore.getState().push({ label, undo })
  toast.success(label, 'Press ⌘Z to undo', {
    label: 'Undo',
    onClick: async () => {
      // Pop the entry we just pushed (or whatever is now on top, if the
      // user fired multiple actions in quick succession — Undo always
      // reverses the most recent thing).
      const entry = useUndoStore.getState().pop()
      if (!entry) return
      try {
        await entry.undo()
      } catch (e) {
        // Re-push so the user can try again, and surface the failure.
        useUndoStore.getState().push({ label: entry.label, undo: entry.undo })
        toast.error('Undo failed', e instanceof Error ? e.message : String(e))
      }
    },
  })
}
