/**
 * Shared setup status hook with automatic request deduplication.
 *
 * This hook ensures that all consumers share a single in-flight request to
 * GET /api/setup. When multiple components mount simultaneously, the hook
 * automatically deduplicates the request — only one network call is made,
 * and all consumers receive the same response.
 *
 * The cached result is invalidated after a successful setup submission or
 * when explicitly refreshed.
 */

import { useCallback, useEffect, useState } from "react";
import { ApiError } from "../api/client";
import { getSetupStatus } from "../api/setup";
import type { SetupStatusResponse } from "../types/setup";

// Module-level cache state — shared across all hook instances.
interface CacheEntry {
  promise: Promise<SetupStatusResponse>;
  timestamp: number;
}

const CACHE_TTL_MS = 30000; // 30 seconds
let cache: CacheEntry | null = null;
const subscribers: Set<() => void> = new Set();

/**
 * Clears the setup status cache and notifies all subscribers.
 *
 * Called after successful setup submission to ensure the next fetch
 * reflects the updated status.
 */
export function invalidateSetupStatus(): void {
  cache = null;
  subscribers.forEach((notify) => {
    notify();
  });
}

interface UseSharedSetupStatusResult {
  /** The cached setup status, or null while loading. */
  status: SetupStatusResponse | null;
  /** Whether the setup status is currently being fetched. */
  loading: boolean;
  /** Error message if the last fetch failed, or null. */
  error: string | null;
  /** Force a refresh of the setup status. */
  refresh: () => Promise<void>;
}

/**
 * Hook to fetch the setup status from a shared, deduplicated cache.
 *
 * All components using this hook share the same in-flight request. If
 * multiple components mount at the same time, only one API call is made.
 *
 * @returns Setup status result with loading and error states.
 */
export function useSharedSetupStatus(): UseSharedSetupStatusResult {
  const [status, setStatus] = useState<SetupStatusResponse | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const refresh = useCallback(async (): Promise<void> => {
    setLoading(true);
    setError(null);

    try {
      // Determine if we should use the cache or make a new request.
      const now = Date.now();
      const isCacheValid = cache && now - cache.timestamp < CACHE_TTL_MS;

      if (!isCacheValid) {
        // Cache miss or expired — create a new in-flight promise.
        cache = {
          promise: getSetupStatus(),
          timestamp: now,
        };
      }

      // Wait for the in-flight request (whether just-created or already pending).
      const result = await cache!.promise;
      setStatus(result);
      setError(null);
    } catch (err: unknown) {
      if (err instanceof ApiError) {
        setError(err.message);
      } else if (err instanceof Error) {
        setError(err.message);
      } else {
        setError("Failed to fetch setup status");
      }
      setStatus(null);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    // Load on mount.
    void refresh();

    // Subscribe to cache invalidation events.
    const unsubscribe = (): void => {
      subscribers.delete(refresh);
    };
    subscribers.add(refresh);
    return unsubscribe;
  }, [refresh]);

  return { status, loading, error, refresh };
}

