import { render, screen, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { useRouter, useSearchParams } from 'next/navigation';
import { ItemBrowseClient } from '../item-browse-client';

vi.mock('next/navigation', () => ({
  useRouter: vi.fn(),
  useSearchParams: vi.fn(),
}));

const browseMock = vi.fn();
vi.mock('@/lib/api', () => ({
  api: { items: { browse: (...a: unknown[]) => browseMock(...a) } },
}));

function wrap(node: React.ReactNode) {
  const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  return <QueryClientProvider client={qc}>{node}</QueryClientProvider>;
}

const categoryTree = [{ id: 1, name: 'Tops', slug: 'tops', children: [] }];

const successResponse = {
  data: [
    {
      id: 'item-1',
      title: 'Vintage Tee',
      brand: 'Levis',
      price: 2500,
      condition: 'EUC',
      image_url: null,
      store: { id: 's1', name: 'Revive' },
    },
  ],
  meta: { total: 1, total_pages: 1, current_page: 1 },
  facets: {},
};

describe('ItemBrowseClient', () => {
  const push = vi.fn();

  beforeEach(() => {
    push.mockClear();
    browseMock.mockReset();
    vi.mocked(useRouter).mockReturnValue({
      push,
      back: vi.fn(),
      forward: vi.fn(),
      refresh: vi.fn(),
      replace: vi.fn(),
      prefetch: vi.fn(),
    } as unknown as ReturnType<typeof useRouter>);
    vi.mocked(useSearchParams).mockReturnValue(
      new URLSearchParams('') as unknown as ReturnType<typeof useSearchParams>,
    );
  });

  it('renders the item count and grid on success', async () => {
    browseMock.mockResolvedValue(successResponse);
    render(wrap(<ItemBrowseClient categoryTree={categoryTree} />));
    await waitFor(() => expect(screen.getByText('1 item')).toBeInTheDocument());
    expect(screen.getByText('Vintage Tee')).toBeInTheDocument();
  });

  it('renders no-results state when API returns an empty list', async () => {
    browseMock.mockResolvedValue({
      data: [],
      meta: { total: 0, total_pages: 0, current_page: 1 },
      facets: {},
    });
    render(wrap(<ItemBrowseClient categoryTree={categoryTree} />));
    await waitFor(() => expect(screen.getByText('0 items')).toBeInTheDocument());
  });

  it('shows error state with retry button when the query fails', async () => {
    browseMock.mockRejectedValue(new Error('boom'));
    render(wrap(<ItemBrowseClient categoryTree={categoryTree} />));
    await waitFor(() =>
      expect(screen.getByText('Something went wrong')).toBeInTheDocument(),
    );
    expect(screen.getByRole('button', { name: /Retry/ })).toBeInTheDocument();
  });

  it('passes parsed search params to api.items.browse', async () => {
    vi.mocked(useSearchParams).mockReturnValue(
      new URLSearchParams('q=denim&sort=price_asc&page=2') as unknown as ReturnType<
        typeof useSearchParams
      >,
    );
    browseMock.mockResolvedValue(successResponse);
    render(wrap(<ItemBrowseClient categoryTree={categoryTree} />));
    await waitFor(() => expect(browseMock).toHaveBeenCalled());
    const args = browseMock.mock.calls[browseMock.mock.calls.length - 1][0];
    expect(args).toMatchObject({ q: 'denim', sort: 'price_asc', page: 2 });
  });
});
