import {describe, expect, it, vi, beforeEach, afterEach} from 'vitest'

import {
  render,
  screen,
  userEvent,
  waitFor,
  mockProduct,
  mockProductVariant,
  mockMinisSDK,
  resetAllMocks,
} from '../../test-utils'

import {
  ProductCard,
  ProductCardContainer,
  ProductCardImageContainer,
  ProductCardImage,
  ProductCardBadge,
  ProductCardFavoriteButton,
  ProductCardInfo,
  ProductCardTitle,
  ProductCardReviewStars,
  ProductCardPrice,
} from './product-card'

// Mock hooks
vi.mock('../../hooks/navigation/useShopNavigation', () => ({
  useShopNavigation: () => ({
    navigateToProduct: mockMinisSDK.navigateToProduct,
    navigateToShop: mockMinisSDK.navigateToShop,
  }),
}))

vi.mock('../../hooks/user/useSavedProductsActions', () => ({
  useSavedProductsActions: () => ({
    saveProduct: mockMinisSDK.saveProduct,
    unsaveProduct: mockMinisSDK.unsaveProduct,
  }),
}))

// Mock formatMoney
vi.mock('../../utils/formatMoney', () => ({
  formatMoney: vi.fn((amount: string, currencyCode: string) => {
    const numAmount = parseFloat(amount)
    return currencyCode === 'USD'
      ? `$${numAmount.toFixed(2)}`
      : `${currencyCode} ${numAmount.toFixed(2)}`
  }),
}))

describe('ProductCard', () => {
  beforeEach(() => {
    resetAllMocks()
  })

  afterEach(() => {
    vi.clearAllMocks()
  })

  describe('Rendering', () => {
    it('renders with default variant', () => {
      const product = mockProduct()
      render(<ProductCard product={product} />)

      // Check for product title
      expect(screen.getByText(product.title)).toBeInTheDocument()

      // Check for price (default from createProduct is $99.99)
      expect(screen.getByText('$99.99')).toBeInTheDocument()

      // Check for image container
      expect(screen.getByRole('img')).toBeInTheDocument()
    })

    it('renders with priceOverlay variant', () => {
      const product = mockProduct()
      render(<ProductCard product={product} variant="priceOverlay" />)

      // Price should be in a badge on top-left
      const badges = screen.getAllByText('$99.99')
      expect(badges.length).toBeGreaterThan(0)

      // Should not show product info section in priceOverlay variant
      expect(screen.queryByText(product.title)).toBeNull()
    })

    it('renders with compact variant', () => {
      const product = mockProduct()
      render(<ProductCard product={product} variant="compact" />)

      // Should not show product info section in compact variant
      const title = screen.queryByText(product.title)
      expect(title).toBeNull()
    })

    it('renders with selected variant', () => {
      const product = mockProduct()
      const selectedVariant = mockProductVariant({
        id: 'variant-2',
        price: {amount: '39.99', currencyCode: 'USD'},
      })

      render(
        <ProductCard
          product={product}
          selectedProductVariant={selectedVariant}
        />
      )

      // Should show selected variant price
      expect(screen.getByText('$39.99')).toBeInTheDocument()
    })

    it('renders with compare at price', () => {
      const product = mockProduct({
        price: {amount: '29.99', currencyCode: 'USD'},
        compareAtPrice: {amount: '39.99', currencyCode: 'USD'},
      })

      render(<ProductCard product={product} />)

      // Should show both prices
      expect(screen.getByText('$29.99')).toBeInTheDocument()
      expect(screen.getByText('$39.99')).toBeInTheDocument()

      // Compare at price should have line-through
      const compareAtPrice = screen.getByText('$39.99')
      expect(compareAtPrice).toHaveClass('line-through')
    })

    it('renders badge when provided', () => {
      const product = mockProduct()
      render(
        <ProductCard
          product={product}
          badgeText="Sale"
          badgeVariant="destructive"
        />
      )

      expect(screen.getByText('Sale')).toBeInTheDocument()
    })

    it('renders without image when product has no featured image', () => {
      const product = mockProduct({featuredImage: undefined})
      render(<ProductCard product={product} />)

      expect(screen.getByText('No Image')).toBeInTheDocument()
    })

    it('renders favorite button by default', () => {
      const product = mockProduct()
      render(<ProductCard product={product} />)

      // Favorite button should be present
      const favoriteButton = screen.getByRole('button')
      expect(favoriteButton).toBeInTheDocument()
    })

    it('hides favorite button when favoriteButtonDisabled is true', () => {
      const product = mockProduct()
      render(<ProductCard product={product} favoriteButtonDisabled />)

      // Favorite button should not be present
      const favoriteButton = screen.queryByRole('button')
      expect(favoriteButton).not.toBeInTheDocument()
    })

    it('shows favorite button when favoriteButtonDisabled is false', () => {
      const product = mockProduct()
      render(<ProductCard product={product} favoriteButtonDisabled={false} />)

      // Favorite button should be present
      const favoriteButton = screen.getByRole('button')
      expect(favoriteButton).toBeInTheDocument()
    })

    it('renders review stars when review data is available', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: 4.5,
          reviewCount: 123,
        },
      })

      render(<ProductCard product={product} />)

      // Should show the review stars and count
      expect(screen.getByText('(123)')).toBeInTheDocument()

      // Check for star elements - ProductReviewStars uses overlay technique with 10 stars total
      const container = screen.getByText('(123)').parentElement
      const stars = container?.querySelectorAll('svg')
      // 5 empty stars + filled stars overlay (4 full + 1 half for 4.5 rating)
      expect(stars).toHaveLength(10)

      // Verify review count is displayed
      expect(screen.getByText('(123)')).toBeInTheDocument()
    })

    it('does not render review stars when review data is missing', () => {
      const product = mockProduct({
        reviewAnalytics: undefined,
      })

      render(<ProductCard product={product} />)

      // Should not show review count
      expect(screen.queryByText(/^\(\d+\)$/)).not.toBeInTheDocument()
    })

    it('does not render review stars when averageRating is null', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: null,
          reviewCount: 100,
        },
      })

      render(<ProductCard product={product} />)

      // Should not show review count
      expect(screen.queryByText('(100)')).not.toBeInTheDocument()
    })

    it('does not render review stars when reviewCount is 0', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: 4.5,
          reviewCount: 0,
        },
      })

      render(<ProductCard product={product} />)

      // Should not show review count
      expect(screen.queryByText('(0)')).not.toBeInTheDocument()
    })

    it('formats large review counts correctly', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: 4.0,
          reviewCount: 1500,
        },
      })

      render(<ProductCard product={product} />)

      // Should format as 1K
      expect(screen.getByText('(1K)')).toBeInTheDocument()
    })

    it('does not show review stars in non-default variants', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: 4.5,
          reviewCount: 123,
        },
      })

      const {rerender} = render(
        <ProductCard product={product} variant="priceOverlay" />
      )

      // Should not show review stars in priceOverlay variant
      expect(screen.queryByText('(123)')).not.toBeInTheDocument()

      rerender(<ProductCard product={product} variant="compact" />)

      // Should not show review stars in compact variant
      expect(screen.queryByText('(123)')).not.toBeInTheDocument()

      rerender(<ProductCard product={product} variant="default" />)

      // Should show review stars in default variant
      expect(screen.getByText('(123)')).toBeInTheDocument()
    })
  })

  describe('Interactions', () => {
    it('handles product click navigation', async () => {
      const user = userEvent.setup()
      const product = mockProduct()
      const onProductClick = vi.fn()

      render(<ProductCard product={product} onProductClick={onProductClick} />)

      const container = screen.getByText(product.title).closest('div')
        ?.parentElement?.parentElement
      await user.click(container!)

      expect(onProductClick).toHaveBeenCalledTimes(1)
      expect(mockMinisSDK.navigateToProduct).toHaveBeenCalledWith({
        productId: product.id,
      })
    })

    it('does not navigate when touchable is false', async () => {
      const user = userEvent.setup()
      const product = mockProduct()
      const onProductClick = vi.fn()

      render(
        <ProductCard
          product={product}
          touchable={false}
          onProductClick={onProductClick}
        />
      )

      const container = screen.getByText(product.title).closest('div')
        ?.parentElement?.parentElement
      await user.click(container!)

      expect(onProductClick).not.toHaveBeenCalled()
      expect(mockMinisSDK.navigateToProduct).not.toHaveBeenCalled()
    })

    it('handles favorite toggle - save product', async () => {
      const user = userEvent.setup()
      const product = mockProduct({isFavorited: false})
      const onFavoriteToggled = vi.fn()
      mockMinisSDK.saveProduct.mockResolvedValue(undefined)

      render(
        <ProductCard product={product} onFavoriteToggled={onFavoriteToggled} />
      )

      const favoriteButton = screen.getByRole('button')
      await user.click(favoriteButton)

      await waitFor(() => {
        expect(mockMinisSDK.saveProduct).toHaveBeenCalledWith({
          productId: product.id,
          shopId: product.shop.id,
          productVariantId: product.defaultVariantId,
        })
        expect(onFavoriteToggled).toHaveBeenCalledWith(true)
      })
    })

    it('handles favorite toggle - unsave product', async () => {
      const user = userEvent.setup()
      const product = mockProduct({isFavorited: true})
      const onFavoriteToggled = vi.fn()
      mockMinisSDK.unsaveProduct.mockResolvedValue(undefined)

      render(
        <ProductCard product={product} onFavoriteToggled={onFavoriteToggled} />
      )

      const favoriteButton = screen.getByRole('button')
      await user.click(favoriteButton)

      await waitFor(() => {
        expect(mockMinisSDK.unsaveProduct).toHaveBeenCalledWith({
          productId: product.id,
          shopId: product.shop.id,
          productVariantId: product.defaultVariantId,
        })
        expect(onFavoriteToggled).toHaveBeenCalledWith(false)
      })
    })

    it('reverts favorite state on error', async () => {
      const user = userEvent.setup()
      const product = mockProduct({isFavorited: false})
      const onFavoriteToggled = vi.fn()

      // Mock save to reject
      mockMinisSDK.saveProduct.mockRejectedValue(new Error('Save failed'))

      render(
        <ProductCard product={product} onFavoriteToggled={onFavoriteToggled} />
      )

      const favoriteButton = screen.getByRole('button')

      // Initially unfilled
      expect(favoriteButton).toHaveClass('bg-button-overlay/30')

      await user.click(favoriteButton)

      // Should call save
      expect(mockMinisSDK.saveProduct).toHaveBeenCalled()

      // Wait for error handling
      await waitFor(() => {
        // Should revert to unfilled state
        expect(favoriteButton).toHaveClass('bg-button-overlay/30')
      })
    })

    it('does not allow favorite toggle when favoriteButtonDisabled is true', async () => {
      const product = mockProduct({isFavorited: false})
      const onFavoriteToggled = vi.fn()

      render(
        <ProductCard
          product={product}
          favoriteButtonDisabled
          onFavoriteToggled={onFavoriteToggled}
        />
      )

      // Button should not exist at all
      const favoriteButton = screen.queryByRole('button')
      expect(favoriteButton).not.toBeInTheDocument()

      // Callbacks should not be called
      expect(onFavoriteToggled).not.toHaveBeenCalled()
      expect(mockMinisSDK.saveProduct).not.toHaveBeenCalled()
      expect(mockMinisSDK.unsaveProduct).not.toHaveBeenCalled()
    })
  })

  describe('Custom Composition', () => {
    it('renders with custom children layout', () => {
      const product = mockProduct()

      render(
        <ProductCard product={product}>
          <ProductCardContainer>
            <div data-testid="custom-layout">
              <ProductCardTitle>Custom Title</ProductCardTitle>
              <ProductCardPrice />
            </div>
          </ProductCardContainer>
        </ProductCard>
      )

      expect(screen.getByTestId('custom-layout')).toBeInTheDocument()
      expect(screen.getByText('Custom Title')).toBeInTheDocument()
      expect(screen.getByText('$99.99')).toBeInTheDocument()
    })

    it('allows individual component usage', () => {
      const product = mockProduct()

      render(
        <ProductCard product={product}>
          <ProductCardContainer>
            <ProductCardImageContainer>
              <ProductCardImage />
              <ProductCardBadge>Custom Badge</ProductCardBadge>
              <ProductCardFavoriteButton />
            </ProductCardImageContainer>
            <ProductCardInfo>
              <ProductCardTitle />
              <ProductCardPrice />
            </ProductCardInfo>
          </ProductCardContainer>
        </ProductCard>
      )

      expect(screen.getByText('Custom Badge')).toBeInTheDocument()
      expect(screen.getByText(product.title)).toBeInTheDocument()
      expect(screen.getByText('$99.99')).toBeInTheDocument()
    })

    it('allows ProductCardReviewStars in custom composition', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: 4.2,
          reviewCount: 50,
        },
      })

      render(
        <ProductCard product={product}>
          <ProductCardContainer>
            <ProductCardImageContainer>
              <ProductCardImage />
            </ProductCardImageContainer>
            <ProductCardInfo>
              <ProductCardTitle />
              <ProductCardReviewStars />
              <ProductCardPrice />
            </ProductCardInfo>
          </ProductCardContainer>
        </ProductCard>
      )

      // Review stars should be rendered
      expect(screen.getByText('(50)')).toBeInTheDocument()

      // Check for star elements - ProductReviewStars uses overlay technique
      const container = screen.getByText('(50)').parentElement
      const stars = container?.querySelectorAll('svg')
      expect(stars).toHaveLength(9) // 5 empty + 3 full + 1 half for 3.5 rating
    })

    it('respects favoriteButtonDisabled in custom composition', () => {
      const product = mockProduct()

      render(
        <ProductCard product={product} favoriteButtonDisabled>
          <ProductCardContainer>
            <ProductCardImageContainer>
              <ProductCardImage />
              <ProductCardFavoriteButton />
            </ProductCardImageContainer>
          </ProductCardContainer>
        </ProductCard>
      )

      // Favorite button should not be rendered even when explicitly included
      const favoriteButton = screen.queryByRole('button')
      expect(favoriteButton).not.toBeInTheDocument()
    })
  })

  describe('Edge Cases', () => {
    it('handles product without variants', () => {
      const product = mockProduct({
        variants: [],
        selectedVariant: undefined,
      })

      render(<ProductCard product={product} />)

      expect(screen.getByText(product.title)).toBeInTheDocument()
      expect(screen.getByText('$99.99')).toBeInTheDocument()
    })
  })

  describe('Accessibility', () => {
    it('has proper alt text for images', () => {
      const product = mockProduct()
      render(<ProductCard product={product} />)

      const img = screen.getByRole('img')
      expect(img).toHaveAttribute(
        'alt',
        product.featuredImage?.altText || product.title
      )
    })

    it('uses product title as alt when image alt text is missing', () => {
      const product = mockProduct({
        featuredImage: {
          url: 'https://example.com/image.jpg',
          altText: '',
          width: 1000,
          height: 1000,
          sensitive: false,
          thumbhash: 'thumbhash',
        },
      })

      render(<ProductCard product={product} />)

      const img = screen.getByRole('img')
      expect(img).toHaveAttribute('alt', product.title)
    })

    it('maintains proper heading hierarchy', () => {
      const product = mockProduct()
      render(<ProductCard product={product} />)

      const heading = screen.getByRole('heading', {level: 3})
      expect(heading).toHaveTextContent(product.title)
    })
  })
})
