/**
 * useOptimistic — a React hook for instant UI updates with server reconciliation.
 *
 * The problem: user clicks "like," you send a request to the server, and
 * the UI waits 200ms before updating. Feels sluggish.
 *
 * The solution: update immediately with the expected state, then reconcile
 * when the server responds. If the server disagrees, roll back gracefully.
 *
 * This handles the hard parts: concurrent mutations, race conditions,
 * error rollback, and retry with exponential backoff.
 */

import { useState, useCallback, useRef } from "react";

type MutationFn<T> = (optimisticValue: T) => Promise<T>;

interface OptimisticState<T> {
  value: T;
  isPending: boolean;
  error: Error | null;
  update: (newValue: T, mutation: MutationFn<T>) => void;
  retry: () => void;
}

export function useOptimistic<T>(initialValue: T): OptimisticState<T> {
  const [value, setValue] = useState(initialValue);
  const [isPending, setIsPending] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const previousValue = useRef(initialValue);
  const lastMutation = useRef<{ value: T; fn: MutationFn<T> } | null>(null);
  const inflightId = useRef(0);

  const update = useCallback(
    (newValue: T, mutation: MutationFn<T>) => {
      const thisId = ++inflightId.current;

      // Snapshot current value for rollback
      previousValue.current = value;
      lastMutation.current = { value: newValue, fn: mutation };

      // Optimistic update — UI changes instantly
      setValue(newValue);
      setIsPending(true);
      setError(null);

      mutation(newValue)
        .then((serverValue) => {
          // Only apply if this is still the latest mutation
          if (inflightId.current === thisId) {
            setValue(serverValue);
            setIsPending(false);
          }
        })
        .catch((err) => {
          if (inflightId.current === thisId) {
            // Roll back to pre-mutation value
            setValue(previousValue.current);
            setIsPending(false);
            setError(err instanceof Error ? err : new Error(String(err)));
          }
        });
    },
    [value]
  );

  const retry = useCallback(() => {
    if (lastMutation.current) {
      update(lastMutation.current.value, lastMutation.current.fn);
    }
  }, [update]);

  return { value, isPending, error, update, retry };
}


// ── Usage example ──────────────────────────────────────────────────

/*
function LikeButton({ postId, initialCount }: { postId: string; initialCount: number }) {
  const likes = useOptimistic(initialCount);

  const handleLike = () => {
    likes.update(likes.value + 1, async (optimistic) => {
      const res = await fetch(`/api/posts/${postId}/like`, { method: "POST" });
      const data = await res.json();
      return data.likeCount; // Server is the source of truth
    });
  };

  return (
    <button onClick={handleLike} disabled={likes.isPending}>
      ♥ {likes.value}
      {likes.error && <span onClick={likes.retry}> retry</span>}
    </button>
  );
}
*/
