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 { MyReviewsClient } from '../my-reviews-client';

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

const myReviewsMock = vi.fn();
vi.mock('@/lib/api', () => ({
  api: {
    reviews: {
      myReviews: (...a: unknown[]) => myReviewsMock(...a),
      update: vi.fn(),
      uploadAttachment: vi.fn(),
    },
  },
}));

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

const baseReview = {
  id: 'rev-recent',
  order_item_id: 'oi-1',
  order_id: 'ord-1',
  store_id: 's-1',
  reviewer_user_id: 'u-1',
  rating: 4,
  rating_item_as_described: 4,
  rating_shipping_speed: 4,
  rating_communication: 4,
  rating_packaging: 4,
  title: 'Nice',
  body: 'Long enough body for review here.',
  state: 'visible' as const,
  edited_at: null,
  hidden_by_admin_id: null,
  hidden_at: null,
  hide_reason: null,
  is_editable: true,
  photos: [],
  created_at: '2026-05-08T10:00:00Z',
  updated_at: '2026-05-01T10:00:00Z',
};

const olderReview = {
  ...baseReview,
  id: 'rev-old',
  is_editable: false,
  created_at: '2026-01-01T10:00:00Z',
};

describe('MyReviewsClient', () => {
  beforeEach(() => {
    myReviewsMock.mockReset();
    vi.mocked(useRouter).mockReturnValue({
      replace: vi.fn(),
      push: vi.fn(),
      back: vi.fn(),
      forward: vi.fn(),
      refresh: 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 empty state when no reviews', async () => {
    myReviewsMock.mockResolvedValue({
      data: [],
      meta: { current_page: 1, last_page: 1, per_page: 20, total: 0, from: null, to: null },
      links: { first: null, last: null, prev: null, next: null },
    });
    render(wrap(<MyReviewsClient />));
    await waitFor(() =>
      expect(screen.getByTestId('my-reviews-empty')).toBeInTheDocument(),
    );
  });

  it('shows the edit button when is_editable === true', async () => {
    myReviewsMock.mockResolvedValue({
      data: [baseReview],
      meta: { current_page: 1, last_page: 1, per_page: 20, total: 1, from: 1, to: 1 },
      links: { first: null, last: null, prev: null, next: null },
    });
    render(wrap(<MyReviewsClient />));
    await waitFor(() =>
      expect(screen.getByText(/Long enough body/i)).toBeInTheDocument(),
    );
    expect(screen.getByTestId('my-review-edit-rev-recent')).toBeInTheDocument();
  });

  it('hides the edit button when is_editable === false', async () => {
    myReviewsMock.mockResolvedValue({
      data: [olderReview],
      meta: { current_page: 1, last_page: 1, per_page: 20, total: 1, from: 1, to: 1 },
      links: { first: null, last: null, prev: null, next: null },
    });
    render(wrap(<MyReviewsClient />));
    await waitFor(() =>
      expect(screen.getByText(/Long enough body/i)).toBeInTheDocument(),
    );
    expect(screen.queryByTestId('my-review-edit-rev-old')).toBeNull();
  });

  it('hides the edit button when is_editable is missing (defensive default)', async () => {
    // Strip is_editable to simulate a legacy payload.
    const reviewWithoutFlag = { ...baseReview, id: 'rev-legacy' } as Record<
      string,
      unknown
    >;
    delete reviewWithoutFlag.is_editable;
    myReviewsMock.mockResolvedValue({
      data: [reviewWithoutFlag],
      meta: { current_page: 1, last_page: 1, per_page: 20, total: 1, from: 1, to: 1 },
      links: { first: null, last: null, prev: null, next: null },
    });
    render(wrap(<MyReviewsClient />));
    await waitFor(() =>
      expect(screen.getByText(/Long enough body/i)).toBeInTheDocument(),
    );
    expect(screen.queryByTestId('my-review-edit-rev-legacy')).toBeNull();
  });

  it('renders pagination controls when last_page > 1', async () => {
    myReviewsMock.mockResolvedValue({
      data: [baseReview],
      meta: { current_page: 1, last_page: 2, per_page: 20, total: 22, from: 1, to: 20 },
      links: { first: null, last: null, prev: null, next: null },
    });
    render(wrap(<MyReviewsClient />));
    await waitFor(() =>
      expect(screen.getByText(/Page 1 of 2/i)).toBeInTheDocument(),
    );
    expect(screen.getByRole('button', { name: /Previous/i })).toBeDisabled();
    expect(screen.getByRole('button', { name: /Next/i })).toBeEnabled();
  });

  it('hides pagination when last_page is 1', async () => {
    myReviewsMock.mockResolvedValue({
      data: [baseReview],
      meta: { current_page: 1, last_page: 1, per_page: 20, total: 1, from: 1, to: 1 },
      links: { first: null, last: null, prev: null, next: null },
    });
    render(wrap(<MyReviewsClient />));
    await waitFor(() =>
      expect(screen.getByText(/Long enough body/i)).toBeInTheDocument(),
    );
    expect(screen.queryByRole('button', { name: /Previous/i })).toBeNull();
    expect(screen.queryByRole('button', { name: /Next/i })).toBeNull();
  });
});
