'use client';
import { useCallback, useState, type Dispatch, type SetStateAction } from 'react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';

/** The URL query-param name every admin/partner worklist uses for its page number. */
const PAGE_PARAM = 'page';

export interface AdminListStateConfig<F> {
  /** Rebuild a filters object from the URL's current search params (only defined keys are read). */
  parse: (params: URLSearchParams) => F;
  /** Serialize a filters object to a plain string record, omitting empty/absent filters. */
  serialize: (filters: F) => Record<string, string>;
  /** The "clear"/default filters value. */
  empty: F;
}

export interface AdminListState<F> {
  /** Local, uncommitted filter edits — typing here never refetches or touches the URL. */
  draft: F;
  setDraft: Dispatch<SetStateAction<F>>;
  /** The filters actually driving the query (and the URL) — set only by `apply`/`clear`. */
  applied: F;
  page: number;
  /** Commits `draft` into `applied`, resets to page 1, and writes both into the URL. */
  apply: () => void;
  /**
   * Commits an EXPLICIT filters value (not `draft`) into `applied` and the URL, resetting to page 1 — for a
   * discrete control (a status tab/select) that should commit the instant it changes. Prefer this over
   * `setDraft(next); apply()` in the same handler: `apply()` closes over the `draft` from the render it was
   * created in, so calling it synchronously right after `setDraft` would commit the OLD value, not `next`.
   * Also updates `draft` to the same value, so the two states never drift apart.
   */
  applyFilters: (filters: F) => void;
  /** Resets both draft and applied to `empty` and writes that (page 1) into the URL. */
  clear: () => void;
  /** Moves to a page, keeping `applied` filters, and writes it into the URL. */
  goToPage: (page: number) => void;
}

/**
 * URL-synced worklist state for the admin/partner consoles — mirrors **applied** filters + the current
 * page into `searchParams` via `router.replace` (never a full navigation, `scroll: false`), so browser
 * back/refresh/a pasted link all reproduce the exact same queue view. Draft filter state stays local per
 * the established draft-vs-applied pattern (tickets/audit): typing in `draft` never refetches and never
 * touches the URL — only `apply`/`clear`/`goToPage` do, mirroring `services/search/filterParams.ts`'s
 * "the filter object is the query key" model onto every other worklist.
 *
 * The initial `applied`/`page` are read from the URL **once, on mount** — after that the URL only ever
 * follows local state, so a user typing in `draft` can never have their input clobbered by a stale
 * `searchParams` re-read. Callers using this hook must be rendered inside a `<Suspense>` boundary
 * (`useSearchParams` requirement) — wrap the page body the way `SearchScreen`/`search/SearchScreen.tsx`
 * does.
 */
export function useAdminListState<F>({
  parse,
  serialize,
  empty,
}: AdminListStateConfig<F>): AdminListState<F> {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();

  const [initial] = useState<{ filters: F; page: number }>(() => {
    const rawPage = Number(searchParams.get(PAGE_PARAM));
    return {
      filters: parse(searchParams),
      page: Number.isInteger(rawPage) && rawPage > 0 ? rawPage : 1,
    };
  });

  const [draft, setDraft] = useState<F>(initial.filters);
  const [applied, setApplied] = useState<F>(initial.filters);
  const [page, setPage] = useState<number>(initial.page);

  const writeUrl = useCallback(
    (filters: F, nextPage: number) => {
      const params = new URLSearchParams(serialize(filters));
      if (nextPage > 1) params.set(PAGE_PARAM, String(nextPage));
      const qs = params.toString();
      router.replace(qs ? `${pathname}?${qs}` : pathname, { scroll: false });
    },
    [pathname, router, serialize],
  );

  const apply = useCallback(() => {
    setApplied(draft);
    setPage(1);
    writeUrl(draft, 1);
  }, [draft, writeUrl]);

  const applyFilters = useCallback(
    (filters: F) => {
      setDraft(filters);
      setApplied(filters);
      setPage(1);
      writeUrl(filters, 1);
    },
    [writeUrl],
  );

  const clear = useCallback(() => {
    setDraft(empty);
    setApplied(empty);
    setPage(1);
    writeUrl(empty, 1);
  }, [empty, writeUrl]);

  const goToPage = useCallback(
    (next: number) => {
      setPage(next);
      writeUrl(applied, next);
    },
    [applied, writeUrl],
  );

  return { draft, setDraft, applied, page, apply, applyFilters, clear, goToPage };
}

/**
 * A detail page's "back" affordance: a real `router.back()` when there is browser history to return to
 * (the common case — a queue row was clicked to get here, so back restores its filter/page/scroll state),
 * falling back to pushing `listHref` when there isn't (a pasted/bookmarked detail link). `listHref` must
 * already be locale-prefixed (`next/navigation`'s router does not add it) — pass
 * `` `/${locale}${ROUTES.ADMIN_TICKETS}` ``.
 */
export function useAdminBackToList(listHref: string): () => void {
  const router = useRouter();
  return useCallback(() => {
    if (typeof window !== 'undefined' && window.history.length > 1) {
      router.back();
    } else {
      router.push(listHref);
    }
  }, [router, listHref]);
}
