import {Product} from '@shopify/shop-minis-platform'
import {describe, expect, it, vi} from 'vitest'

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

import {AddToCartButton} from './add-to-cart'

// Mock hooks
vi.mock('../../internal/useShopCartActions', () => ({
  useShopCartActions: () => ({
    addToCart: mockMinisSDK.addToCart,
    buyProduct: mockMinisSDK.buyProduct,
  }),
}))

vi.mock('../../hooks', () => ({
  useShopNavigation: () => ({
    navigateToProduct: mockMinisSDK.navigateToProduct,
  }),
  useErrorToast: () => ({
    showErrorToast: vi.fn(),
  }),
}))

describe('AddToCartButton', () => {
  const mockProduct: Product = {
    id: 'gid://shopify/Product/123',
    title: 'Test Product',
    reviewAnalytics: {
      averageRating: null,
      reviewCount: null,
    },
    shop: {
      id: 'gid://shopify/Shop/1',
      name: 'Test Shop',
    },
    defaultVariantId: 'gid://shopify/ProductVariant/456',
    isFavorited: false,
    price: {
      amount: '10.00',
      currencyCode: 'USD',
    },
    variants: [
      {
        id: 'gid://shopify/ProductVariant/456',
        title: 'Default',
        isFavorited: false,
        availableForSale: true,
        price: {
          amount: '10.00',
          currencyCode: 'USD',
        },
        image: {
          url: 'https://example.com/variant-image.jpg',
          altText: null,
          width: null,
          height: null,
        },
      },
    ],
  }

  const defaultProps = {
    product: mockProduct,
    productVariantId: 'gid://shopify/ProductVariant/456',
  }

  // eslint-disable-next-line jest/require-top-level-describe
  beforeEach(() => {
    resetAllMocks()
  })

  it('renders with default text', () => {
    render(<AddToCartButton {...defaultProps} />)

    expect(screen.getByRole('button')).toBeInTheDocument()
    expect(screen.getByText('Add to cart')).toBeInTheDocument()
  })

  it('renders with required props', () => {
    render(<AddToCartButton {...defaultProps} />)

    const button = screen.getByRole('button')
    expect(button).toBeInTheDocument()
  })

  it('respects disabled prop', () => {
    render(<AddToCartButton {...defaultProps} disabled />)

    const button = screen.getByRole('button')
    expect(button).toBeDisabled()
  })

  it('applies custom className', () => {
    render(<AddToCartButton {...defaultProps} className="custom-class" />)

    const button = screen.getByRole('button')
    expect(button).toHaveClass('custom-class')
  })

  it('renders with different sizes', () => {
    const {rerender} = render(<AddToCartButton {...defaultProps} size="sm" />)

    expect(screen.getByRole('button')).toBeInTheDocument()

    rerender(<AddToCartButton {...defaultProps} size="default" />)
    expect(screen.getByRole('button')).toBeInTheDocument()

    rerender(<AddToCartButton {...defaultProps} size="lg" />)
    expect(screen.getByRole('button')).toBeInTheDocument()
  })

  it('renders with discount codes prop', () => {
    const discountCodes = ['SUMMER20', 'FREESHIP']

    render(<AddToCartButton {...defaultProps} discountCodes={discountCodes} />)

    expect(screen.getByRole('button')).toBeInTheDocument()
  })

  it('calls addToCart when clicked and not a referral product', async () => {
    const user = userEvent.setup()
    mockMinisSDK.addToCart.mockResolvedValueOnce({ok: true})

    render(<AddToCartButton {...defaultProps} />)

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

    expect(mockMinisSDK.addToCart).toHaveBeenCalledWith({
      productId: mockProduct.id,
      productVariantId: defaultProps.productVariantId,
      quantity: 1,
      discountCodes: undefined,
      variantImageUrl: 'https://example.com/variant-image.jpg',
    })
  })

  it('navigates to product page when product is referral', async () => {
    const user = userEvent.setup()
    const referralProduct: Product = {...mockProduct, referral: true}

    render(<AddToCartButton {...defaultProps} product={referralProduct} />)

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

    expect(mockMinisSDK.navigateToProduct).toHaveBeenCalledWith({
      productId: referralProduct.id,
    })
    expect(mockMinisSDK.addToCart).not.toHaveBeenCalled()
  })

  it('shows success animation after adding to cart', async () => {
    const user = userEvent.setup()
    mockMinisSDK.addToCart.mockResolvedValueOnce({ok: true})

    render(<AddToCartButton {...defaultProps} />)

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

    // Check for success state (Added to cart text)
    await screen.findByText('Added to cart')
  })

  it('handles add to cart error gracefully', async () => {
    const user = userEvent.setup()
    mockMinisSDK.addToCart.mockRejectedValueOnce(new Error('Failed'))

    render(<AddToCartButton {...defaultProps} />)

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

    expect(mockMinisSDK.addToCart).toHaveBeenCalled()
  })

  it('does not call addToCart when disabled', async () => {
    const user = userEvent.setup()

    render(<AddToCartButton {...defaultProps} disabled />)

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

    expect(mockMinisSDK.addToCart).not.toHaveBeenCalled()
  })

  it('handles product without variants array', async () => {
    const user = userEvent.setup()
    mockMinisSDK.addToCart.mockResolvedValueOnce({ok: true})

    const productWithoutVariants: Product = {
      ...mockProduct,
      variants: undefined,
    }

    render(
      <AddToCartButton
        product={productWithoutVariants}
        productVariantId="gid://shopify/ProductVariant/456"
      />
    )

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

    expect(mockMinisSDK.addToCart).toHaveBeenCalledWith({
      productId: productWithoutVariants.id,
      productVariantId: 'gid://shopify/ProductVariant/456',
      quantity: 1,
      discountCodes: undefined,
      variantImageUrl: undefined,
    })
  })

  it('handles product without matching variant in array', async () => {
    const user = userEvent.setup()
    mockMinisSDK.addToCart.mockResolvedValueOnce({ok: true})

    const productWithDifferentVariant: Product = {
      ...mockProduct,
      variants: [
        {
          id: 'gid://shopify/ProductVariant/999',
          title: 'Different',
          isFavorited: false,
          availableForSale: true,
          price: {
            amount: '15.00',
            currencyCode: 'USD',
          },
        },
      ],
    }

    render(
      <AddToCartButton
        product={productWithDifferentVariant}
        productVariantId="gid://shopify/ProductVariant/456"
      />
    )

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

    expect(mockMinisSDK.addToCart).toHaveBeenCalledWith({
      productId: productWithDifferentVariant.id,
      productVariantId: 'gid://shopify/ProductVariant/456',
      quantity: 1,
      discountCodes: undefined,
      variantImageUrl: undefined,
    })
  })

  it('handles product without shop data', async () => {
    const user = userEvent.setup()
    mockMinisSDK.addToCart.mockResolvedValueOnce({ok: true})

    const productWithoutShop: Product = {
      ...mockProduct,
      shop: undefined as any,
    }

    render(
      <AddToCartButton
        product={productWithoutShop}
        productVariantId="gid://shopify/ProductVariant/456"
      />
    )

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

    expect(mockMinisSDK.addToCart).toHaveBeenCalledWith({
      productId: productWithoutShop.id,
      productVariantId: 'gid://shopify/ProductVariant/456',
      quantity: 1,
      discountCodes: undefined,
      variantImageUrl: 'https://example.com/variant-image.jpg',
    })
  })

  describe('sold-out state', () => {
    const soldOutProduct: Product = {
      ...mockProduct,
      variants: [
        {
          id: 'gid://shopify/ProductVariant/456',
          title: 'Default',
          isFavorited: false,
          availableForSale: false,
          price: {
            amount: '10.00',
            currencyCode: 'USD',
          },
        },
      ],
    }

    it('shows "Sold out" text when matching variant is not available for sale', () => {
      render(
        <AddToCartButton
          product={soldOutProduct}
          productVariantId="gid://shopify/ProductVariant/456"
        />
      )

      expect(screen.getByText('Sold out')).toBeInTheDocument()
      expect(screen.queryByText('Add to cart')).not.toBeInTheDocument()
    })

    it('disables the button when matching variant is sold out', () => {
      render(
        <AddToCartButton
          product={soldOutProduct}
          productVariantId="gid://shopify/ProductVariant/456"
        />
      )

      expect(screen.getByRole('button')).toBeDisabled()
    })

    it('does not call addToCart when matching variant is sold out', async () => {
      const user = userEvent.setup()

      render(
        <AddToCartButton
          product={soldOutProduct}
          productVariantId="gid://shopify/ProductVariant/456"
        />
      )

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

      expect(mockMinisSDK.addToCart).not.toHaveBeenCalled()
    })

    it('referral products show "View product" and stay clickable even when sold out', async () => {
      const user = userEvent.setup()
      const referralSoldOutProduct: Product = {
        ...soldOutProduct,
        referral: true,
      }

      render(
        <AddToCartButton
          product={referralSoldOutProduct}
          productVariantId="gid://shopify/ProductVariant/456"
        />
      )

      expect(screen.getByText('View product')).toBeInTheDocument()
      expect(screen.queryByText('Sold out')).not.toBeInTheDocument()

      const button = screen.getByRole('button')
      expect(button).toBeEnabled()

      await user.click(button)
      expect(mockMinisSDK.navigateToProduct).toHaveBeenCalledWith({
        productId: referralSoldOutProduct.id,
      })
      expect(mockMinisSDK.addToCart).not.toHaveBeenCalled()
    })

    it('falls back to selectedVariant when variants array is missing', () => {
      const productWithSelectedVariant: Product = {
        ...mockProduct,
        variants: undefined,
        selectedVariant: {
          id: 'gid://shopify/ProductVariant/456',
          title: 'Default',
          isFavorited: false,
          availableForSale: false,
          price: {amount: '10.00', currencyCode: 'USD'},
        },
      }

      render(
        <AddToCartButton
          product={productWithSelectedVariant}
          productVariantId="gid://shopify/ProductVariant/456"
        />
      )

      expect(screen.getByText('Sold out')).toBeInTheDocument()
      expect(screen.getByRole('button')).toBeDisabled()
    })
  })
})
