import { useCallback, useMemo } from 'react';
import { useParams, useSearchParams } from 'react-router';
import { DEFAULT_INVOICES_SECTION, isInvoicesSection, type InvoicesSectionId } from './shared';

// The URL names the open section and the selected record — not a panel channel. See docs/navigation-audit.md.
// InvoicesScreen redirects anything unrecognised, so the fallback here only covers the instant before that
// lands.

export function useInvoicesSection(): InvoicesSectionId {
  const { section } = useParams();
  return isInvoicesSection(section) ? section : DEFAULT_INVOICES_SECTION;
}

export type ListFilters = {
  /** The record whose detail pane is open. `?selected=` because the list stays on screen beside it. */
  selected: number | null;
  search: string;
  status: string | null;
  customerId: number | null;
  fromDate: string | null;
  toDate: string | null;
  page: number;
  isFiltered: boolean;
};

/**
 * Read/write the list query string.
 *
 * Every setter drops `page` back to 1 — changing a filter while on page 4 of the old result set otherwise
 * lands you on a page that may not exist, and upstream answers that with an empty array rather than an
 * error, which reads as "no results" for a filter that has plenty.
 *
 * Selection is `replace: true` so clicking through a list does not bury the previous screen under a dozen
 * history entries; filter changes push normally, because backing out of a filter is a thing people want.
 */
export function useListFilters() {
  const [searchParams, setSearchParams] = useSearchParams();

  const filters = useMemo<ListFilters>(() => {
    const num = (key: string) => {
      const raw = searchParams.get(key);
      const n = raw == null ? NaN : Number(raw);
      return Number.isInteger(n) && n > 0 ? n : null;
    };
    const status = searchParams.get('status');
    const search = searchParams.get('q') ?? '';
    const customerId = num('customer');
    const fromDate = searchParams.get('from');
    const toDate = searchParams.get('to');

    return {
      selected: num('selected'),
      search,
      status,
      customerId,
      fromDate,
      toDate,
      page: num('page') ?? 1,
      isFiltered: Boolean(search || status || customerId || fromDate || toDate),
    };
  }, [searchParams]);

  const patch = useCallback(
    (changes: Record<string, string | number | null>, opts?: { replace?: boolean; keepPage?: boolean }) => {
      setSearchParams(
        (prev) => {
          const next = new URLSearchParams(prev);
          for (const [key, value] of Object.entries(changes)) {
            if (value === null || value === '') next.delete(key);
            else next.set(key, String(value));
          }
          if (!opts?.keepPage && !('page' in changes)) next.delete('page');
          return next;
        },
        { replace: opts?.replace ?? false },
      );
    },
    [setSearchParams],
  );

  const select = useCallback(
    (id: number | null) => patch({ selected: id }, { replace: true, keepPage: true }),
    [patch],
  );

  const setPage = useCallback((page: number) => patch({ page: page <= 1 ? null : page }, { keepPage: true }), [patch]);

  const clearFilters = useCallback(
    () => patch({ q: null, status: null, customer: null, from: null, to: null }),
    [patch],
  );

  return { filters, patch, select, setPage, clearFilters };
}
