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

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

const listMock = vi.fn();
const resolveMock = vi.fn();
vi.mock('@/lib/api', () => ({
  api: {
    admin: {
      reviewReports: {
        list: (...a: unknown[]) => listMock(...a),
        resolve: (...a: unknown[]) => resolveMock(...a),
      },
    },
  },
}));

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

const report = {
  id: 'rpt-12345abc',
  review_id: 'rev-1',
  reason: 'inappropriate' as const,
  reason_text: 'Uses offensive language about packaging.',
  state: 'open' as const,
  action: null,
  resolution_note: null,
  resolved_at: null,
  created_at: '2026-05-07T10:00:00Z',
  updated_at: '2026-05-07T10:00:00Z',
  reporter: { id: 'u-2', name: 'Jane Reporter' },
  resolved_by: null,
  review: {
    id: 'rev-1',
    store_id: 'store-1',
    store_name: 'Revive Boutique',
    reviewer_name: 'Bob Buyer',
    rating: 2,
    title: 'Not great',
    body: 'Hated the packaging — also some unkind words here.',
    state: 'visible' as const,
    created_at: '2026-05-01T10:00:00Z',
  },
};

function paginatedResponse(
  rows: typeof report[],
  overrides: Partial<{ current_page: number; last_page: number; total: number }> = {},
) {
  return {
    data: rows,
    meta: {
      current_page: overrides.current_page ?? 1,
      last_page: overrides.last_page ?? 1,
      per_page: 20,
      total: overrides.total ?? rows.length,
      from: rows.length ? 1 : null,
      to: rows.length || null,
    },
    links: { first: null, last: null, prev: null, next: null },
  };
}

describe('AdminReviewsClient', () => {
  const replace = vi.fn();
  let currentParams = new URLSearchParams('');

  beforeEach(() => {
    listMock.mockReset();
    resolveMock.mockReset();
    replace.mockReset();
    currentParams = new URLSearchParams('');
    vi.mocked(useRouter).mockReturnValue({
      replace: (url: string) => {
        replace(url);
        const qs = url.split('?')[1] ?? '';
        currentParams = new URLSearchParams(qs);
      },
      push: vi.fn(),
      back: vi.fn(),
      forward: vi.fn(),
      refresh: vi.fn(),
      prefetch: vi.fn(),
    } as unknown as ReturnType<typeof useRouter>);
    vi.mocked(useSearchParams).mockImplementation(
      () => currentParams as unknown as ReturnType<typeof useSearchParams>,
    );
    vi.mocked(usePathname).mockReturnValue('/admin/reviews');
  });

  it('renders the row when reports exist and shows a Review button', async () => {
    listMock.mockResolvedValue(paginatedResponse([report]));
    render(wrap(<AdminReviewsClient />));
    await waitFor(() =>
      expect(screen.getByText('Revive Boutique')).toBeInTheDocument(),
    );
    expect(screen.getByText('Bob Buyer')).toBeInTheDocument();
    expect(screen.getByText('Jane Reporter')).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /^Review$/i })).toBeInTheDocument();
  });

  it('shows an empty state when there are no reports', async () => {
    listMock.mockResolvedValue(paginatedResponse([]));
    render(wrap(<AdminReviewsClient />));
    await waitFor(() =>
      expect(screen.getByText(/No reports/i)).toBeInTheDocument(),
    );
  });

  it('defaults to state=open and calls list with state=open', async () => {
    listMock.mockResolvedValue(paginatedResponse([]));
    render(wrap(<AdminReviewsClient />));
    await waitFor(() => expect(listMock).toHaveBeenCalled());
    const args = listMock.mock.calls[0][0];
    expect(args).toMatchObject({ state: 'open' });
  });

  it('toggling Resolved sets state=resolved in URL and re-queries', async () => {
    listMock.mockResolvedValue(paginatedResponse([]));
    const { rerender } = render(wrap(<AdminReviewsClient />));
    fireEvent.click(screen.getByRole('button', { name: /^Resolved$/i }));
    expect(replace).toHaveBeenCalledWith('/admin/reviews?state=resolved');

    rerender(wrap(<AdminReviewsClient />));
    await waitFor(() => {
      const lastArgs = listMock.mock.calls[listMock.mock.calls.length - 1][0];
      expect(lastArgs).toMatchObject({ state: 'resolved' });
    });
  });

  it('clicking Review opens the resolve dialog', async () => {
    listMock.mockResolvedValue(paginatedResponse([report]));
    render(wrap(<AdminReviewsClient />));
    await waitFor(() =>
      expect(screen.getByRole('button', { name: /^Review$/i })).toBeInTheDocument(),
    );
    fireEvent.click(screen.getByRole('button', { name: /^Review$/i }));
    expect(
      await screen.findByRole('dialog', { name: /Resolve review report/i }),
    ).toBeInTheDocument();
  });

  it('shows pagination when last_page > 1 and advances page', async () => {
    listMock.mockResolvedValue(
      paginatedResponse([report], { current_page: 1, last_page: 3, total: 45 }),
    );
    render(wrap(<AdminReviewsClient />));
    await waitFor(() =>
      expect(screen.getByText(/Page 1 of 3/i)).toBeInTheDocument(),
    );
    fireEvent.click(screen.getByRole('button', { name: /Next/i }));
    expect(replace).toHaveBeenCalledWith('/admin/reviews?page=2');
  });
});
