import { renderHook, act } from '@testing-library/react';

const mockReplace = jest.fn();
const mockBack = jest.fn();
const mockPush = jest.fn();
let mockSearchParams = new URLSearchParams();

jest.mock('next/navigation', () => ({
  useRouter: () => ({ replace: mockReplace, back: mockBack, push: mockPush }),
  usePathname: () => '/fa/admin/tickets',
  useSearchParams: () => mockSearchParams,
}));

import { useAdminListState, useAdminBackToList } from './useAdminListState';

interface Filters {
  status?: string;
}

const CONFIG = {
  parse: (params: URLSearchParams): Filters => ({ status: params.get('status') ?? undefined }),
  serialize: (filters: Filters): Record<string, string> => (filters.status ? { status: filters.status } : {}),
  empty: {} as Filters,
};

describe('useAdminListState', () => {
  beforeEach(() => {
    mockReplace.mockReset();
    mockBack.mockReset();
    mockPush.mockReset();
    mockSearchParams = new URLSearchParams();
  });

  it('reads the initial applied filters + page from the URL', () => {
    mockSearchParams = new URLSearchParams('status=open&page=3');
    const { result } = renderHook(() => useAdminListState(CONFIG));
    expect(result.current.applied).toEqual({ status: 'open' });
    expect(result.current.draft).toEqual({ status: 'open' });
    expect(result.current.page).toBe(3);
  });

  it('defaults to page 1 with empty filters when the URL carries none', () => {
    const { result } = renderHook(() => useAdminListState(CONFIG));
    expect(result.current.applied).toEqual({ status: undefined });
    expect(result.current.page).toBe(1);
  });

  it('editing draft never touches applied or the URL', () => {
    const { result } = renderHook(() => useAdminListState(CONFIG));
    act(() => result.current.setDraft({ status: 'closed' }));
    expect(result.current.draft).toEqual({ status: 'closed' });
    expect(result.current.applied).toEqual({ status: undefined });
    expect(mockReplace).not.toHaveBeenCalled();
  });

  it('apply commits draft into applied, resets to page 1, and writes the URL', () => {
    const { result } = renderHook(() => useAdminListState(CONFIG));
    act(() => result.current.setDraft({ status: 'closed' }));
    act(() => result.current.apply());
    expect(result.current.applied).toEqual({ status: 'closed' });
    expect(result.current.page).toBe(1);
    expect(mockReplace).toHaveBeenCalledWith('/fa/admin/tickets?status=closed', { scroll: false });
  });

  it('clear resets draft + applied and writes a bare URL', () => {
    mockSearchParams = new URLSearchParams('status=open');
    const { result } = renderHook(() => useAdminListState(CONFIG));
    act(() => result.current.clear());
    expect(result.current.draft).toEqual({});
    expect(result.current.applied).toEqual({});
    expect(mockReplace).toHaveBeenCalledWith('/fa/admin/tickets', { scroll: false });
  });

  it('applyFilters commits an explicit value atomically (never a stale draft)', () => {
    const { result } = renderHook(() => useAdminListState(CONFIG));
    // No setDraft call first — applyFilters must not depend on draft having been set beforehand.
    act(() => result.current.applyFilters({ status: 'closed' }));
    expect(result.current.draft).toEqual({ status: 'closed' });
    expect(result.current.applied).toEqual({ status: 'closed' });
    expect(result.current.page).toBe(1);
    expect(mockReplace).toHaveBeenCalledWith('/fa/admin/tickets?status=closed', { scroll: false });
  });

  it('goToPage keeps applied filters and appends page to the URL', () => {
    mockSearchParams = new URLSearchParams('status=open');
    const { result } = renderHook(() => useAdminListState(CONFIG));
    act(() => result.current.goToPage(2));
    expect(result.current.page).toBe(2);
    expect(mockReplace).toHaveBeenCalledWith('/fa/admin/tickets?status=open&page=2', { scroll: false });
  });
});

describe('useAdminBackToList', () => {
  beforeEach(() => {
    mockReplace.mockReset();
    mockBack.mockReset();
    mockPush.mockReset();
  });

  it('calls router.back() when there is browser history', () => {
    Object.defineProperty(window, 'history', { value: { length: 3 }, configurable: true });
    const { result } = renderHook(() => useAdminBackToList('/fa/admin/tickets'));
    act(() => result.current());
    expect(mockBack).toHaveBeenCalledTimes(1);
    expect(mockPush).not.toHaveBeenCalled();
  });

  it('falls back to pushing the list href when there is no history', () => {
    Object.defineProperty(window, 'history', { value: { length: 1 }, configurable: true });
    const { result } = renderHook(() => useAdminBackToList('/fa/admin/tickets'));
    act(() => result.current());
    expect(mockPush).toHaveBeenCalledWith('/fa/admin/tickets');
    expect(mockBack).not.toHaveBeenCalled();
  });
});
