import { useCallback, useEffect, useRef, useState } from "react";
import apiFetch from "../utils/api";

/** Lock holder as enriched by the detail endpoints (offers, issued orders). */
export interface DocumentLockInfo {
  user_id: number;
  username: string;
  full_name: string;
}

/**
 * Client side of the document edit lock shared by the offer / issued-order
 * detail pages (extracted from OfferDetail). The server exposes the same
 * trio under the entity base URL: POST `:base/lock`, `:base/heartbeat`,
 * `:base/unlock` (lock expires after 30 s without a heartbeat — 3 missed
 * 10 s beats, so background-tab interval throttling can't hand the lock away).
 *
 * Responsibilities:
 * - `lockedBy` state — the page hydrates it from the detail response via
 *   `setLockedBy(detail.locked_by ?? null)`.
 * - `acquire()` — fire-and-forget lock acquisition; the page calls it from
 *   its one-shot hydration effect when the doc is editable, the user holds
 *   the edit permission and nobody else holds the lock.
 * - 10 s heartbeat while `enabled` (and not locked by another user), with
 *   hard-401 self-stop: a 401 here means the access token expired AND the
 *   refresh failed (apiFetch retries recoverable 401s itself), so we stop
 *   the interval instead of spamming a dead session every 10 s.
 * - unlock on unmount/disable, with an AbortController so a stale in-flight
 *   unlock can be cancelled by the next one.
 */
export function useDocumentLock({
  baseUrl,
  enabled,
}: {
  /** Entity endpoint base (e.g. `/api/admin/offers/42`); null in create mode. */
  baseUrl: string | null;
  /**
   * External conditions for holding the lock: edit mode + editable status +
   * edit permission. The hook adds `!isLockedByOther` itself.
   */
  enabled: boolean;
}) {
  const [lockedBy, setLockedBy] = useState<DocumentLockInfo | null>(null);
  const heartbeatRef = useRef<ReturnType<typeof setInterval> | null>(null);
  const unlockAbortRef = useRef<AbortController | null>(null);
  const isLockedByOther = lockedBy !== null;

  const acquire = useCallback(() => {
    if (!baseUrl) return;
    apiFetch(`${baseUrl}/lock`, { method: "POST" }).catch(() => {});
  }, [baseUrl]);

  // Heartbeat to keep the lock alive + release on unmount/disable.
  useEffect(() => {
    if (!baseUrl || !enabled || isLockedByOther) return;

    heartbeatRef.current = setInterval(() => {
      apiFetch(`${baseUrl}/heartbeat`, { method: "POST" })
        .then((res) => {
          if (res.status === 401 && heartbeatRef.current) {
            clearInterval(heartbeatRef.current);
            heartbeatRef.current = null;
          }
        })
        .catch(() => {});
    }, 10 * 1000); // every 10 seconds

    return () => {
      if (heartbeatRef.current) clearInterval(heartbeatRef.current);
      if (unlockAbortRef.current) unlockAbortRef.current.abort();
      const controller = new AbortController();
      unlockAbortRef.current = controller;
      apiFetch(`${baseUrl}/unlock`, {
        method: "POST",
        signal: controller.signal,
      }).catch(() => {});
    };
  }, [baseUrl, enabled, isLockedByOther]);

  return { lockedBy, isLockedByOther, setLockedBy, acquire };
}
