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 { StoreDetailClient } from '../store-detail-client';
import type { StorePublic } from '@alqove/api-client';

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

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

const EMPTY_SUMMARY = {
  data: {
    average_rating: null,
    review_count: 0,
    distribution: { '1': 0, '2': 0, '3': 0, '4': 0, '5': 0 },
    dimensions: {
      item_as_described: null,
      shipping_speed: null,
      communication: null,
      packaging: null,
    },
  },
};

const EMPTY_REVIEWS = {
  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 },
};

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

const store: StorePublic = {
  id: 'store-1',
  name: 'Revive Boutique',
  city: 'Portland',
  state: 'OR',
  is_verified: true,
} as unknown as StorePublic;

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

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

  beforeEach(() => {
    push.mockClear();
    browseMock.mockReset();
    ratingSummaryMock.mockReset();
    storeReviewsMock.mockReset();
    ratingSummaryMock.mockResolvedValue(EMPTY_SUMMARY);
    storeReviewsMock.mockResolvedValue(EMPTY_REVIEWS);
    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 store header with name, location, and verified pill', async () => {
    browseMock.mockResolvedValue({
      data: [],
      meta: { total: 0, total_pages: 0, current_page: 1 },
      facets: {},
    });
    render(wrap(<StoreDetailClient store={store} categoryTree={categoryTree} />));
    expect(screen.getByText('Revive Boutique')).toBeInTheDocument();
    expect(screen.getByText(/Portland, OR/)).toBeInTheDocument();
    expect(screen.getByText(/Verified/)).toBeInTheDocument();
  });

  it('forces store_id filter to the current store when querying items', async () => {
    browseMock.mockResolvedValue({
      data: [],
      meta: { total: 0, total_pages: 0, current_page: 1 },
      facets: {},
    });
    render(wrap(<StoreDetailClient store={store} categoryTree={categoryTree} />));
    await waitFor(() => expect(browseMock).toHaveBeenCalled());
    const args = browseMock.mock.calls[browseMock.mock.calls.length - 1][0];
    expect(args.store_id).toEqual(['store-1']);
  });

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

  it('renders empty reviews state when store has no reviews', async () => {
    browseMock.mockResolvedValue({
      data: [],
      meta: { total: 0, total_pages: 0, current_page: 1 },
      facets: {},
    });
    render(wrap(<StoreDetailClient store={store} categoryTree={categoryTree} />));
    await waitFor(() =>
      expect(screen.getByTestId('store-reviews-empty')).toBeInTheDocument(),
    );
  });

  it('renders rating badge, dimension breakdown, and review list when reviews exist', async () => {
    browseMock.mockResolvedValue({
      data: [],
      meta: { total: 0, total_pages: 0, current_page: 1 },
      facets: {},
    });
    ratingSummaryMock.mockResolvedValue({
      data: {
        average_rating: 4.6,
        review_count: 12,
        distribution: { '1': 0, '2': 0, '3': 1, '4': 3, '5': 8 },
        dimensions: {
          item_as_described: 4.7,
          shipping_speed: 4.5,
          communication: 4.6,
          packaging: 4.6,
        },
      },
    });
    storeReviewsMock.mockResolvedValue({
      data: [
        {
          id: 'rev-1',
          store_id: 'store-1',
          buyer: { name: 'Jamie B.' },
          rating: 5,
          rating_item_as_described: 5,
          rating_shipping_speed: 5,
          rating_communication: 5,
          rating_packaging: 5,
          title: 'Great',
          body: 'A wonderful experience.',
          state: 'visible' as const,
          edited_at: null,
          photos: [],
          created_at: '2026-05-01T10:00:00Z',
        },
      ],
      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(<StoreDetailClient store={store} categoryTree={categoryTree} />));
    await waitFor(() =>
      expect(screen.getByTestId('store-reviews-list')).toBeInTheDocument(),
    );
    expect(screen.getByTestId('dimension-breakdown')).toBeInTheDocument();
    expect(screen.getByText('Jamie B.')).toBeInTheDocument();
    expect(screen.getByText(/A wonderful experience/)).toBeInTheDocument();
  });
});
