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

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

import {ProductLink} from './product-link'

// 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('../../lib/formatMoney', () => ({
  formatMoney: vi.fn((amount: string, currencyCode: string) => {
    const numAmount = parseFloat(amount)
    return currencyCode === 'USD'
      ? `$${numAmount.toFixed(2)}`
      : `${currencyCode} ${numAmount.toFixed(2)}`
  }),
}))

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

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

  describe('Rendering', () => {
    it('renders product information correctly', () => {
      const product = mockProduct({
        title: 'Test Product',
        price: {amount: '29.99', currencyCode: 'USD'},
      })

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

      expect(screen.getByText('Test Product')).toBeInTheDocument()
      expect(screen.getByText('$29.99')).toBeInTheDocument()
    })

    it('renders product image when available', () => {
      const product = mockProduct({
        featuredImage: {
          url: 'https://example.com/image.jpg',
          altText: 'Product Image',
          width: 100,
          height: 100,
          sensitive: false,
          thumbhash: null,
        },
      })

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

      const image = screen.getByRole('img')
      expect(image).toHaveAttribute('src', 'https://example.com/image.jpg')
      expect(image).toHaveAttribute('alt', 'Product Image')
    })

    it('shows no image placeholder when image is not available', () => {
      const product = mockProduct({
        featuredImage: undefined,
      })

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

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

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

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

      const image = screen.getByRole('img')
      expect(image).toHaveAttribute('alt', 'My Product')
    })

    it('displays discount pricing when compareAtPrice exists', () => {
      const product = mockProduct({
        price: {amount: '19.99', currencyCode: 'USD'},
        compareAtPrice: {amount: '29.99', currencyCode: 'USD'},
      })

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

      // Discounted price should be shown
      expect(screen.getByText('$19.99')).toBeInTheDocument()
      // Original price with line-through
      const originalPrice = screen.getByText('$29.99')
      expect(originalPrice).toBeInTheDocument()
      expect(originalPrice).toHaveClass('line-through')
    })

    it('shows regular price when no discount', () => {
      const product = mockProduct({
        price: {amount: '29.99', currencyCode: 'USD'},
        compareAtPrice: undefined,
      })

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

      expect(screen.getByText('$29.99')).toBeInTheDocument()
      expect(screen.queryByText(/line-through/)).not.toBeInTheDocument()
    })

    it('displays review rating and count when available', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: 4.5,
          reviewCount: 123,
        },
      })

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

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

      // Should show star rating
      const stars = document.querySelectorAll('svg')
      expect(stars.length).toBeGreaterThan(0)
    })

    it('does not show rating when review data is missing', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: null,
          reviewCount: 0,
        },
      })

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

      expect(
        screen.queryByTestId('product-link-rating')
      ).not.toBeInTheDocument()
    })

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

      // Should have favorite button
      const buttons = screen.getAllByRole('button')
      expect(buttons.length).toBeGreaterThan(0)
    })

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

      // Should not have favorite button
      expect(screen.queryByRole('button')).not.toBeInTheDocument()
    })
  })

  describe('Interactions', () => {
    it('navigates to product on click', async () => {
      const user = userEvent.setup()
      const product = mockProduct()
      const onClick = vi.fn()

      render(<ProductLink product={product} onClick={onClick} />)

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

      expect(mockMinisSDK.navigateToProduct).toHaveBeenCalledWith({
        productId: product.id,
      })
      expect(onClick).toHaveBeenCalledWith(product)
    })

    it('saves product when favorite button is clicked (unfavorited to favorited)', async () => {
      const user = userEvent.setup()
      const product = mockProduct({isFavorited: false})
      mockMinisSDK.saveProduct.mockResolvedValue(undefined)

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

      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,
        })
      })
    })

    it('unsaves product when favorite button is clicked (favorited to unfavorited)', async () => {
      const user = userEvent.setup()
      const product = mockProduct({isFavorited: true})
      mockMinisSDK.unsaveProduct.mockResolvedValue(undefined)

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

      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,
        })
      })
    })

    it('uses selected variant ID for favorite actions when available', async () => {
      const user = userEvent.setup()
      const product = mockProduct({
        isFavorited: false,
        selectedVariant: {
          id: 'selected-variant-id',
          title: 'Selected Variant',
          isFavorited: false,
          price: {amount: '29.99', currencyCode: 'USD'},
          compareAtPrice: null,
          image: null,
        },
      })
      mockMinisSDK.saveProduct.mockResolvedValue(undefined)

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

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

      await waitFor(() => {
        expect(mockMinisSDK.saveProduct).toHaveBeenCalledWith({
          productId: product.id,
          shopId: product.shop.id,
          productVariantId: 'selected-variant-id',
        })
      })
    })

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

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

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

      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 and revert
      await waitFor(() => {
        // Should revert to unfilled state
        expect(favoriteButton).toHaveClass('bg-button-overlay/30')
      })
    })

    it('prevents event propagation on favorite button click', async () => {
      const user = userEvent.setup()
      const product = mockProduct()
      const onClick = vi.fn()
      mockMinisSDK.saveProduct.mockResolvedValue(undefined)

      render(<ProductLink product={product} onClick={onClick} />)

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

      // Should save product but not trigger navigation
      expect(mockMinisSDK.saveProduct).toHaveBeenCalled()
      expect(onClick).not.toHaveBeenCalled()
      expect(mockMinisSDK.navigateToProduct).not.toHaveBeenCalled()
    })
  })

  describe('Star Rating Display', () => {
    it('displays correct number of filled stars based on rating', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: 3.7,
          reviewCount: 50,
        },
      })

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

      // Check that rating is displayed
      expect(screen.getByText('(50)')).toBeInTheDocument()

      // Check for star icons (there's at least one star svg)
      const stars = document.querySelectorAll('svg')
      expect(stars.length).toBeGreaterThan(0)
    })

    it('shows all empty stars for zero rating', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: 0,
          reviewCount: 10,
        },
      })

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

      const stars = document.querySelectorAll('svg')
      const filledStars = Array.from(stars).filter(
        star => star.getAttribute('fill') === 'currentColor'
      )
      expect(filledStars).toHaveLength(0)
    })

    it('shows all filled stars for 5 star rating', () => {
      const product = mockProduct({
        reviewAnalytics: {
          averageRating: 5,
          reviewCount: 100,
        },
      })

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

      const stars = document.querySelectorAll('svg')
      const filledStars = Array.from(stars).filter(
        star => star.getAttribute('fill') === 'currentColor'
      )
      expect(filledStars).toHaveLength(5)
    })
  })

  describe('Edge Cases', () => {
    it('handles product without price', () => {
      const product = mockProduct({
        price: undefined,
      })

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

      // Should still render without crashing
      expect(screen.getByText(product.title)).toBeInTheDocument()
    })

    it('handles very long product titles', () => {
      const product = mockProduct({
        title:
          'This is a very long product title that should be truncated in the UI to prevent layout issues',
      })

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

      // Title should be rendered and have truncate classes
      const titleElement = screen.getByText(product.title)
      expect(titleElement).toBeInTheDocument()
      expect(titleElement.className).toContain('text-ellipsis')
    })

    it('handles rapid favorite toggling', async () => {
      const user = userEvent.setup()
      const product = mockProduct({isFavorited: false})

      mockMinisSDK.saveProduct.mockResolvedValue(undefined)
      mockMinisSDK.unsaveProduct.mockResolvedValue(undefined)

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

      const favoriteButton = screen.getByRole('button')

      // Rapid clicks
      await user.click(favoriteButton)
      await user.click(favoriteButton)
      await user.click(favoriteButton)

      // Should handle all clicks gracefully
      expect(mockMinisSDK.saveProduct.mock.calls.length).toBeGreaterThan(0)
    })

    it('handles product without shop data', () => {
      const product = mockProduct({
        shop: {id: '', name: ''},
      })

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

      // Should still render
      expect(screen.getByText(product.title)).toBeInTheDocument()
    })
  })

  describe('Visual States', () => {
    it('applies hover/tap animation styles', () => {
      const product = mockProduct()
      const {container} = render(<ProductLink product={product} />)

      // Check that touchable wrapper exists
      const touchable = container.querySelector('[data-touchable="true"]')
      expect(touchable).toBeTruthy()
    })

    it('shows correct favorite button state', async () => {
      const product = mockProduct({isFavorited: true})
      render(<ProductLink product={product} />)

      const favoriteButton = screen.getByRole('button')
      expect(favoriteButton).toHaveClass('bg-primary')
    })

    it('applies correct layout styles', () => {
      const product = mockProduct()
      render(<ProductLink product={product} />)

      const image = screen.getByRole('img')
      expect(image.parentElement).toHaveClass('h-16', 'w-16')
    })
  })

  describe('Accessibility', () => {
    it('has proper heading hierarchy', () => {
      const product = mockProduct()
      render(<ProductLink product={product} />)

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

    it('provides proper alt text for images', () => {
      const product = mockProduct({
        featuredImage: {
          url: 'https://example.com/image.jpg',
          altText: 'Descriptive alt text',
          width: 100,
          height: 100,
          sensitive: false,
          thumbhash: null,
        },
      })

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

      const image = screen.getByRole('img')
      expect(image).toHaveAttribute('alt', 'Descriptive alt text')
    })
  })
})
