"use client";

import type { PoResult } from "@/app/api/po";
import type { RecordsRes } from "@/app/api/utils";
import { clientAuthFetch } from "@/app/utils/clientAuthFetch";
import { NEXT_PUBLIC_API_URL } from "@/config/api";
import { mapPoResultToListRow } from "@/components/PoWorkbench/search/poWorkbenchMapPoResult";
import {
  buildWorkbenchPoListSearchParams,
  PO_WORKBENCH_LIST_PAGE_SIZE,
} from "@/components/PoWorkbench/search/poWorkbenchPoListQuery";
import type {
  PoWorkbenchAdvancedFilters,
  PoWorkbenchListRow,
} from "@/components/PoWorkbench/types";
import { useCallback, useEffect, useRef, useState } from "react";

const PO_LIST_PATH = "/po/list";

/** Bumps after each keystroke delay; triggers a new `/po/list` first page. */
function useDebouncedValue(value: string, delayMs: number): string {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const id = window.setTimeout(() => setDebounced(value), delayMs);
    return () => window.clearTimeout(id);
  }, [value, delayMs]);
  return debounced;
}

async function fetchPoListPage(
  poNumber: string,
  advanced: PoWorkbenchAdvancedFilters,
  pageNum: number,
  pageSize: number,
  signal?: AbortSignal,
): Promise<{ rows: PoWorkbenchListRow[]; total: number }> {
  const params = buildWorkbenchPoListSearchParams(
    poNumber,
    advanced,
    pageNum,
    pageSize,
  );
  const url = `${NEXT_PUBLIC_API_URL}${PO_LIST_PATH}?${params.toString()}`;
  const response = await clientAuthFetch(url, { method: "GET", signal });
  if (!response.ok) {
    throw new Error(`PO list failed: HTTP ${response.status}`);
  }
  const body = (await response.json()) as RecordsRes<PoResult[]>;
  const records = Array.isArray(body.records) ? body.records : [];
  const total = typeof body.total === "number" ? body.total : records.length;
  return {
    rows: records.map(mapPoResultToListRow),
    total,
  };
}

export interface UsePoWorkbenchListSearchArgs {
  poNumberQuery: string;
  advancedFilters: PoWorkbenchAdvancedFilters;
  /** Delay after typing in the PO field before hitting the API (ms). */
  poNumberDebounceMs?: number;
  pageSize?: number;
}

export interface UsePoWorkbenchListSearchResult {
  listRows: readonly PoWorkbenchListRow[];
  /** Total rows matching filters (from API), not how many are loaded yet. */
  totalMatches: number;
  isLoading: boolean;
  isLoadingMore: boolean;
  loadError: string | null;
  hasMore: boolean;
  /** Loads the next page; no-op if nothing more to load or a request is in flight. */
  loadMore: () => void;
}

/**
 * Fetches `/po/list` for the workbench: first page when debounced filters change, then `loadMore` appends pages.
 * Stale HTTP responses are ignored via `searchGenerationRef`. Does not use legacy `PoSearch`.
 */
export function usePoWorkbenchListSearch({
  poNumberQuery,
  advancedFilters,
  poNumberDebounceMs = 350,
  pageSize = PO_WORKBENCH_LIST_PAGE_SIZE,
}: UsePoWorkbenchListSearchArgs): UsePoWorkbenchListSearchResult {
  const debouncedPoNumber = useDebouncedValue(
    poNumberQuery,
    poNumberDebounceMs,
  );

  const [listRows, setListRows] = useState<PoWorkbenchListRow[]>([]);
  const [totalMatches, setTotalMatches] = useState(0);
  const [isLoading, setIsLoading] = useState(true);
  const [isLoadingMore, setIsLoadingMore] = useState(false);
  const [loadError, setLoadError] = useState<string | null>(null);
  /** Highest 1-based page number successfully merged into listRows. */
  const loadedPageRef = useRef(0);
  const loadMoreInFlightRef = useRef(false);
  /** Incremented on each new search so stale responses never mutate state. */
  const searchGenerationRef = useRef(0);
  const abortRef = useRef<AbortController | null>(null);

  useEffect(() => {
    abortRef.current?.abort();
    const controller = new AbortController();
    abortRef.current = controller;

    const generation = ++searchGenerationRef.current;
    loadedPageRef.current = 0;
    loadMoreInFlightRef.current = false;

    setIsLoading(true);
    setLoadError(null);
    setListRows([]);
    setTotalMatches(0);

    (async () => {
      try {
        const { rows, total } = await fetchPoListPage(
          debouncedPoNumber,
          advancedFilters,
          1,
          pageSize,
          controller.signal,
        );
        if (generation !== searchGenerationRef.current) {
          return;
        }
        loadedPageRef.current = 1;
        setListRows(rows);
        setTotalMatches(total);
      } catch (e) {
        if (
          controller.signal.aborted ||
          generation !== searchGenerationRef.current
        ) {
          return;
        }
        const message = e instanceof Error ? e.message : "Unknown error";
        setLoadError(message);
        setListRows([]);
        setTotalMatches(0);
      } finally {
        if (generation === searchGenerationRef.current) {
          setIsLoading(false);
        }
      }
    })();

    return () => {
      controller.abort();
    };
  }, [advancedFilters, debouncedPoNumber, pageSize]);

  const hasMore = listRows.length < totalMatches;

  const loadMore = useCallback(async () => {
    if (!hasMore || isLoading || loadMoreInFlightRef.current) {
      return;
    }
    const generation = searchGenerationRef.current;
    const nextPage = loadedPageRef.current + 1;
    loadMoreInFlightRef.current = true;
    setIsLoadingMore(true);
    setLoadError(null);

    try {
      const { rows } = await fetchPoListPage(
        debouncedPoNumber,
        advancedFilters,
        nextPage,
        pageSize,
      );
      if (generation !== searchGenerationRef.current) {
        return;
      }
      loadedPageRef.current = nextPage;
      setListRows((prev) => {
        const seen = new Set(prev.map((r) => r.id));
        const merged = [...prev];
        for (const row of rows) {
          if (!seen.has(row.id)) {
            seen.add(row.id);
            merged.push(row);
          }
        }
        return merged;
      });
    } catch (e) {
      if (generation === searchGenerationRef.current) {
        const message = e instanceof Error ? e.message : "Unknown error";
        setLoadError(message);
      }
    } finally {
      loadMoreInFlightRef.current = false;
      if (generation === searchGenerationRef.current) {
        setIsLoadingMore(false);
      }
    }
  }, [advancedFilters, debouncedPoNumber, hasMore, isLoading, pageSize]);

  return {
    listRows,
    totalMatches,
    isLoading,
    isLoadingMore,
    loadError,
    hasMore,
    loadMore,
  };
}
