"use client";
import { useCallback, useRef, useState } from "react";
import { trpc } from "~/lib/trpc/client.js";
import { useInvalidateTimeline } from "./useInvalidatePlanningViews.js";
import type { AllocationMovedSnapshot } from "./useTimelineDrag.js";

export type { AllocationMovedSnapshot };

/** A single allocation move or a batch shift of multiple allocations */
export type HistoryEntry =
  | { type: "single"; snapshot: AllocationMovedSnapshot }
  | { type: "batch"; allocationIds: string[]; daysDelta: number; mode: "move" | "resize-start" | "resize-end" };

const DEFAULT_MAX_HISTORY = 50;

export function useAllocationHistory() {
  const [canUndo, setCanUndo] = useState(false);
  const [canRedo, setCanRedo] = useState(false);
  const past = useRef<HistoryEntry[]>([]);
  const future = useRef<HistoryEntry[]>([]);

  const invalidateTimeline = useInvalidateTimeline();

  // Configurable max steps from system settings
  const { data: settings } = trpc.settings.getSystemSettings.useQuery(undefined, {
    staleTime: 60_000,
  });
  const maxHistory = settings?.timelineUndoMaxSteps ?? DEFAULT_MAX_HISTORY;

  const updateMutation = trpc.timeline.updateAllocationInline.useMutation({
    onSuccess: invalidateTimeline,
  });

  const batchShiftMutation = trpc.timeline.batchShiftAllocations.useMutation({
    onSuccess: invalidateTimeline,
  });

  const push = useCallback((snapshot: AllocationMovedSnapshot) => {
    past.current = [...past.current.slice(-(maxHistory - 1)), { type: "single", snapshot }];
    future.current = [];
    setCanUndo(true);
    setCanRedo(false);
  }, [maxHistory]);

  const pushBatch = useCallback((allocationIds: string[], daysDelta: number, mode: "move" | "resize-start" | "resize-end" = "move") => {
    past.current = [...past.current.slice(-(maxHistory - 1)), { type: "batch", allocationIds, daysDelta, mode }];
    future.current = [];
    setCanUndo(true);
    setCanRedo(false);
  }, [maxHistory]);

  const undo = useCallback(async () => {
    const last = past.current[past.current.length - 1];
    if (!last) return;
    past.current = past.current.slice(0, -1);
    future.current = [last, ...future.current];
    setCanUndo(past.current.length > 0);
    setCanRedo(true);

    if (last.type === "single") {
      await updateMutation.mutateAsync({
        allocationId: last.snapshot.mutationAllocationId,
        startDate: last.snapshot.before.startDate,
        endDate: last.snapshot.before.endDate,
      });
    } else {
      // Batch: reverse the shift (for resize modes, reverse means shifting the same edge back)
      const reverseMode = last.mode === "resize-start" ? "resize-start"
        : last.mode === "resize-end" ? "resize-end"
        : "move";
      await batchShiftMutation.mutateAsync({
        allocationIds: last.allocationIds,
        daysDelta: -last.daysDelta,
        mode: reverseMode,
      });
    }
  }, [updateMutation, batchShiftMutation]);

  const redo = useCallback(async () => {
    const next = future.current[0];
    if (!next) return;
    future.current = future.current.slice(1);
    past.current = [...past.current, next];
    setCanUndo(true);
    setCanRedo(future.current.length > 0);

    if (next.type === "single") {
      await updateMutation.mutateAsync({
        allocationId: next.snapshot.mutationAllocationId,
        startDate: next.snapshot.after.startDate,
        endDate: next.snapshot.after.endDate,
      });
    } else {
      // Batch: re-apply the shift
      await batchShiftMutation.mutateAsync({
        allocationIds: next.allocationIds,
        daysDelta: next.daysDelta,
        mode: next.mode,
      });
    }
  }, [updateMutation, batchShiftMutation]);

  return { push, pushBatch, undo, redo, canUndo, canRedo };
}
