// Shared GET-and-render-state hook — Task 9. Every reports/drill-through
// screen needs the same loading/error/retry dance Dashboard.tsx (Task 8)
// wrote out by hand; this factors it into one hook so five call sites don't
// duplicate the cancellation/retry bookkeeping. Callers build the full path
// (including query string) themselves, so a query-param change (a new asOf
// date, a new page offset, ...) is just a new `path` string, which is all
// the dependency array needs to know to refetch.

import { useCallback, useEffect, useRef, useState } from "react";
import { apiGet, ApiError } from "./api";

export interface ApiGetState<T> {
  data: T | null;
  loading: boolean;
  error: string | null;
  retry: () => void;
}

/**
 * Fetches `path` under `companyId` whenever either changes. Pass `path` as
 * null to skip fetching entirely (e.g. while a required id/param isn't known
 * yet) — the hook then reports loading: false, data: null, no error.
 *
 * `data` deliberately survives a manual retry() of the SAME path (so a
 * transient error on an already-loaded screen doesn't blank it out from
 * under the user) — but it's cleared the moment `path`/`companyId` changes
 * to a genuinely different resource. Without that distinction, navigating
 * from one entity to another that 404s (e.g. #/entry/6551 -> a bad id typed
 * into #/entry/:id) would leave the PREVIOUS entry's lines rendered on
 * screen underneath the "we couldn't find that" banner — a real, observed
 * bug during Task 9's manual verification against real seeded data.
 */
export function useApiGet<T>(path: string | null, companyId: string | null | undefined): ApiGetState<T> {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [reloadToken, setReloadToken] = useState(0);
  const resourceKey = path && companyId ? `${companyId}:${path}` : null;
  const prevKeyRef = useRef<string | null>(null);

  useEffect(() => {
    if (!path || !companyId) {
      setData(null);
      setLoading(false);
      setError(null);
      prevKeyRef.current = null;
      return;
    }

    if (prevKeyRef.current !== resourceKey) {
      setData(null);
    }
    prevKeyRef.current = resourceKey;

    let cancelled = false;
    setLoading(true);
    setError(null);
    apiGet<T>(path, companyId)
      .then((body) => {
        if (!cancelled) setData(body);
      })
      .catch((err: unknown) => {
        if (!cancelled) {
          setError(err instanceof ApiError ? err.message : "Something went wrong loading this.");
        }
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });
    return () => {
      cancelled = true;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps -- resourceKey is derived from path/companyId
  }, [path, companyId, reloadToken, resourceKey]);

  const retry = useCallback(() => setReloadToken((n) => n + 1), []);

  return { data, loading, error, retry };
}
