import { act, render, screen, waitFor, fireEvent, within } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import { ItemDetailClient } from '../item-detail-client';
import type { ItemDetailData } from '@alqove/types';

const addItemMock = vi.fn();
const searchMock = vi.fn();
const ratingSummaryMock = vi.fn();
vi.mock('@/lib/api', () => ({
  api: {
    cart: { addItem: (...a: unknown[]) => addItemMock(...a) },
    items: {
      browse: (...a: unknown[]) => searchMock(...a),
      search: (...a: unknown[]) => searchMock(...a),
    },
    reviews: {
      storeRatingSummary: (...a: unknown[]) => ratingSummaryMock(...a),
    },
  },
}));

const EMPTY_RATING_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,
    },
  },
};

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

const baseItem: ItemDetailData = {
  id: 'item-1',
  title: 'Vintage Tee',
  brand: 'Levis',
  price: 2500,
  original_retail: 5000,
  condition: 'EUC',
  description: 'Soft and well loved.',
  size: 'M',
  colors: ['blue'],
  status: 'active',
  category: {
    id: 3,
    name: 'Tops',
    slug: 'clothing-tops',
  },
  category_path: [
    { id: 1, name: 'Clothing', slug: 'clothing' },
    { id: 3, name: 'Tops', slug: 'clothing-tops' },
  ],
  images: [],
  store: {
    id: 'store-1',
    name: 'Revive',
    city: 'Portland',
    state: 'OR',
    is_verified: true,
    is_suspended: false,
    average_rating: 4.8,
    review_count: 37,
    rating_breakdown: {
      item_as_described: 4.9,
      shipping_speed: 4.7,
      communication: 4.8,
      packaging: 4.8,
    },
    policies: {
      flat_shipping_rate: 899,
      free_shipping_threshold: 10000,
      minimum_order_amount: 2500,
      processing_days: 2,
      return_window_days: 14,
      return_policy_text: 'Returns accepted within 14 days.',
    },
  },
} as unknown as ItemDetailData;

describe('ItemDetailClient', () => {
  beforeEach(() => {
    addItemMock.mockReset();
    searchMock.mockReset();
    searchMock.mockResolvedValue({ data: [], meta: { total: 0, total_pages: 0 } });
    ratingSummaryMock.mockReset();
    ratingSummaryMock.mockResolvedValue(EMPTY_RATING_SUMMARY);
  });

  afterEach(() => {
    vi.unstubAllGlobals();
    vi.restoreAllMocks();
  });

  it('renders title, brand, price, condition, and description', async () => {
    render(wrap(<ItemDetailClient item={baseItem} />));
    expect(screen.getByRole('heading', { name: 'Vintage Tee' })).toBeInTheDocument();
    expect(screen.getByRole('navigation', { name: 'Breadcrumb' })).toBeInTheDocument();
    expect(screen.getByRole('link', { name: 'Tops' })).toHaveAttribute(
      'href',
      '/items?category_slug=clothing-tops',
    );
    expect(screen.getAllByText('Levis').length).toBeGreaterThan(0);
    expect(screen.getAllByText('Excellent Used Condition').length).toBeGreaterThan(0);
    expect(screen.getByText('Soft and well loved.')).toBeInTheDocument();
    expect(screen.getByText('Only 1 available')).toBeInTheDocument();
    expect(screen.getByRole('region', { name: 'Item details' })).toBeInTheDocument();
    expect(screen.getByRole('region', { name: 'Condition confidence' })).toBeInTheDocument();
    expect(screen.getByRole('region', { name: 'Revive' })).toBeInTheDocument();
    expect(screen.getByRole('region', { name: 'Shipping and returns' })).toBeInTheDocument();
    expect(screen.getByText('Free shipping over $100.00')).toBeInTheDocument();
    expect(screen.getByText('14-day returns')).toBeInTheDocument();
  });

  it('add-to-cart success shows confirmation toast', async () => {
    addItemMock.mockResolvedValue({ data: {} });
    render(wrap(<ItemDetailClient item={baseItem} />));
    fireEvent.click(screen.getByRole('button', { name: /Add to Cart/ }));
    await waitFor(() =>
      expect(screen.getByText('Added to cart!')).toBeInTheDocument(),
    );
    expect(addItemMock).toHaveBeenCalledWith('item-1');
  });

  it('shows compact purchase bar after the buy box reaches the header area', async () => {
    addItemMock.mockResolvedValue({ data: {} });
    const rectSpy = vi
      .spyOn(Element.prototype, 'getBoundingClientRect')
      .mockReturnValue({
        x: 0,
        y: 120,
        top: 120,
        right: 0,
        bottom: 520,
        left: 0,
        width: 0,
        height: 400,
        toJSON: () => ({}),
      } as DOMRect);

    render(wrap(<ItemDetailClient item={baseItem} />));

    expect(
      screen.queryByRole('complementary', { name: /Vintage Tee quick purchase/i }),
    ).toBeNull();

    rectSpy.mockReturnValue({
      x: 0,
      y: 48,
      top: 48,
      right: 0,
      bottom: 448,
      left: 0,
      width: 0,
      height: 400,
      toJSON: () => ({}),
    } as DOMRect);

    act(() => {
      window.dispatchEvent(new Event('scroll'));
    });

    const purchaseBar = screen.getByRole('complementary', {
      name: /Vintage Tee quick purchase/i,
    });
    fireEvent.click(within(purchaseBar).getByRole('button', { name: /Add to Cart/ }));

    await waitFor(() => expect(addItemMock).toHaveBeenCalledWith('item-1'));
  });

  it('add-to-cart failure shows unavailable toast', async () => {
    addItemMock.mockRejectedValue(new Error('gone'));
    render(wrap(<ItemDetailClient item={baseItem} />));
    fireEvent.click(screen.getByRole('button', { name: /Add to Cart/ }));
    await waitFor(() =>
      expect(screen.getByText('Item is no longer available')).toBeInTheDocument(),
    );
  });

  it('store badge links to the store page', () => {
    render(wrap(<ItemDetailClient item={baseItem} />));
    const storeLink = screen
      .getAllByRole('link')
      .find((a) => a.getAttribute('href') === '/stores/store-1');
    expect(storeLink).toBeDefined();
  });

  it('renders more-from-store and similar recommendation rails from browse results', async () => {
    searchMock.mockImplementation((params: { store_id?: string[]; category_slug?: string }) => {
      if (params.store_id?.includes('store-1')) {
        return Promise.resolve({
          data: [
            {
              id: 'item-1',
              title: 'Vintage Tee',
              price: 2500,
              condition: 'EUC',
              image_url: null,
              store: { id: 'store-1', name: 'Revive' },
            },
            {
              id: 'store-item',
              title: 'Store Jacket',
              brand: 'Levis',
              price: 4200,
              condition: 'GUC',
              image_url: null,
              store: { id: 'store-1', name: 'Revive' },
            },
          ],
          meta: { total: 2, total_pages: 1 },
        });
      }

      return Promise.resolve({
        data: [
          {
            id: 'item-1',
            title: 'Vintage Tee',
            price: 2500,
            condition: 'EUC',
            image_url: null,
            store: { id: 'store-1', name: 'Revive' },
          },
          {
            id: 'store-item',
            title: 'Store Jacket',
            brand: 'Levis',
            price: 4200,
            condition: 'GUC',
            image_url: null,
            store: { id: 'store-1', name: 'Revive' },
          },
          {
            id: 'similar-item',
            title: 'Similar Hoodie',
            brand: 'Levis',
            price: 3600,
            condition: 'EUC',
            image_url: null,
            store: { id: 'store-2', name: 'Thread House' },
          },
        ],
        meta: { total: 3, total_pages: 1 },
      });
    });

    render(wrap(<ItemDetailClient item={baseItem} />));

    await waitFor(() => expect(searchMock).toHaveBeenCalledTimes(2));

    expect(searchMock).toHaveBeenCalledWith(
      expect.objectContaining({ store_id: ['store-1'], per_page: 8 }),
    );
    expect(searchMock).toHaveBeenCalledWith(
      expect.objectContaining({
        category_slug: 'clothing-tops',
        brand: ['Levis'],
        size: ['M'],
        per_page: 8,
      }),
    );

    expect(screen.getByRole('region', { name: 'More from Revive' })).toBeInTheDocument();
    expect(screen.getByRole('region', { name: 'Similar items' })).toBeInTheDocument();
    await waitFor(() => expect(screen.getByText('Store Jacket')).toBeInTheDocument());
    expect(screen.getByText('Similar Hoodie')).toBeInTheDocument();
    expect(screen.getAllByText('Store Jacket')).toHaveLength(1);
  });

  it('hides rating badge when store has zero reviews', () => {
    render(
      wrap(
        <ItemDetailClient
          item={{
            ...baseItem,
            store: {
              ...baseItem.store,
              average_rating: null,
              review_count: 0,
              rating_breakdown: {
                item_as_described: null,
                shipping_speed: null,
                communication: null,
                packaging: null,
              },
            },
          }}
        />,
      ),
    );

    expect(screen.queryByTestId('rating-badge')).toBeNull();
    expect(screen.getByText('No reviews yet')).toBeInTheDocument();
  });

  it('shows rating badge with avg + count from the item detail payload', () => {
    render(
      wrap(
        <ItemDetailClient
          item={{
            ...baseItem,
            store: {
              ...baseItem.store,
              average_rating: 4.8,
              review_count: 142,
              rating_breakdown: {
                item_as_described: 4.9,
                shipping_speed: 4.7,
                communication: 4.8,
                packaging: 4.7,
              },
            },
          }}
        />,
      ),
    );

    const ratingBadge = screen.getByTestId('rating-badge');

    expect(within(ratingBadge).getByText('4.8')).toBeInTheDocument();
    expect(within(ratingBadge).getByText(/142 reviews/i)).toBeInTheDocument();
  });

  it('falls back to top-level rating fields for older item detail payloads', () => {
    render(
      wrap(
        <ItemDetailClient
          item={{
            ...baseItem,
            store: {
              id: 'store-1',
              name: 'Revive',
              city: 'Portland',
              state: 'OR',
            },
            store_average_rating: 4.6,
            store_review_count: 12,
            rating_item_as_described: 4.7,
            rating_shipping_speed: 4.5,
            rating_communication: 4.6,
            rating_packaging: 4.8,
          } as ItemDetailData}
        />,
      ),
    );

    const ratingBadge = screen.getByTestId('rating-badge');

    expect(within(ratingBadge).getByText('4.6')).toBeInTheDocument();
    expect(within(ratingBadge).getByText(/12 reviews/i)).toBeInTheDocument();
  });
});
