import * as React from 'react'
import {useCallback, useContext, useMemo, useState} from 'react'

import {type Product, type ProductVariant} from '@shopify/shop-minis-platform'

import {useShopNavigation} from '../../hooks/navigation/useShopNavigation'
import {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'
import {ProductReviewStars} from '../../internal/components/product-review-stars'
import {useProductImpression} from '../../internal/useProductImpression'
import {cn} from '../../lib/utils'
import {formatMoney} from '../../utils/formatMoney'
import {Image} from '../atoms/image'
import {ProductVariantPrice} from '../atoms/product-variant-price'
import {Touchable} from '../atoms/touchable'
import {Badge} from '../ui/badge'

import {FavoriteButton} from './favorite-button'

// Context definition
interface ProductCardContextValue {
  // Core data
  product: Product
  selectedProductVariant?: ProductVariant

  // UI configuration
  variant: 'default' | 'priceOverlay' | 'compact'
  touchable: boolean
  badgeText?: string
  badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'
  favoriteButtonDisabled: boolean
  reviewsDisabled: boolean

  // State
  isFavorited: boolean

  // Actions
  onClick: () => void
  onFavoriteToggle: () => void
}

const ProductCardContext = React.createContext<
  ProductCardContextValue | undefined
>(undefined)

function useProductCardContext() {
  const context = useContext(ProductCardContext)
  if (!context) {
    throw new Error(
      'ProductCard components must be used within a ProductCard provider'
    )
  }
  return context
}

// Primitive components (building blocks)
function ProductCardContainer({
  className,
  ...props
}: React.ComponentProps<'div'>) {
  const {touchable, onClick} = useProductCardContext()

  const content = (
    <div
      className={cn(
        'relative size-full overflow-hidden rounded-xl border-0 isolate',
        className
      )}
      {...props}
    />
  )

  if (touchable && onClick) {
    return (
      <Touchable
        onClick={onClick}
        whileTap={{opacity: 0.7}}
        transition={{
          opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},
        }}
      >
        {content}
      </Touchable>
    )
  }

  return content
}

function ProductCardImageContainer({
  className,
  ...props
}: React.ComponentProps<'div'>) {
  const {variant} = useProductCardContext()

  return (
    <div
      data-slot="product-card-image-container"
      className={cn(
        'relative overflow-hidden rounded-xl border border-gray-200',
        'aspect-square',
        variant === 'compact' ? 'min-h-[104px]' : 'min-h-[134px]',
        className
      )}
      {...props}
    />
  )
}

function ProductCardImage({className, ...props}: React.ComponentProps<'img'>) {
  const {product, selectedProductVariant} = useProductCardContext()

  // Derive display image locally
  const displayImage = selectedProductVariant?.image || product.featuredImage
  const src = displayImage?.url
  const alt = displayImage?.altText || product.title
  const thumbhash = product.featuredImage?.thumbhash

  const renderImageElement = useCallback(
    (src: string) => {
      const imageElement = thumbhash ? (
        <Image
          data-slot="product-card-image"
          src={src}
          alt={alt}
          aspectRatio={1}
          thumbhash={thumbhash}
          objectFit="cover"
          className={cn('size-full', className)}
          {...props}
        />
      ) : (
        <img
          data-slot="product-card-image"
          src={src}
          alt={alt}
          className={cn('size-full object-cover', className)}
          {...props}
        />
      )

      return imageElement
    },
    [alt, className, props, thumbhash]
  )

  return (
    <div className="bg-gray-100 flex items-center justify-center size-full">
      {src ? (
        renderImageElement(src)
      ) : (
        <div className="text-gray-400 text-sm w-full text-center">No Image</div>
      )}
    </div>
  )
}

function ProductCardBadge({
  className,
  position = 'bottom-left',
  variant,
  children,
  ...props
}: React.ComponentProps<typeof Badge> & {
  position?: 'top-left' | 'bottom-left'
}) {
  const {badgeText, badgeVariant} = useProductCardContext()
  // If no children provided, use badgeText from context
  const content = children || badgeText

  if (!content) return null

  return (
    <div
      className={cn(
        'absolute z-10',
        position === 'top-left' ? 'top-3 left-3' : 'bottom-2 left-2'
      )}
    >
      <Badge
        variant={variant ?? badgeVariant ?? 'none'}
        className={cn(
          !badgeVariant &&
            !variant &&
            'bg-black/50 text-white border-transparent',
          'rounded',
          className
        )}
        {...props}
      >
        {content}
      </Badge>
    </div>
  )
}

function ProductCardFavoriteButton({
  className,
  ...props
}: React.ComponentProps<'div'>) {
  const {isFavorited, favoriteButtonDisabled, onFavoriteToggle} =
    useProductCardContext()
  if (favoriteButtonDisabled) return null

  return (
    <div className={cn('absolute bottom-3 right-3 z-10', className)} {...props}>
      <FavoriteButton onClick={onFavoriteToggle} filled={isFavorited} />
    </div>
  )
}

function ProductCardInfo({className, ...props}: React.ComponentProps<'div'>) {
  const {variant} = useProductCardContext()
  if (variant !== 'default') {
    return null
  }

  return (
    <div
      data-testid="product-card-info"
      className={cn('px-1 pt-2 pb-0 space-y-1', className)}
      {...props}
    />
  )
}

function ProductCardTitle({
  className,
  children,
  ...props
}: React.ComponentProps<'h3'>) {
  const {product} = useProductCardContext()
  return (
    <h3
      data-slot="product-card-title"
      className={cn(
        'text-sm font-medium leading-tight text-gray-900',
        'truncate overflow-hidden whitespace-nowrap text-ellipsis',
        className
      )}
      {...props}
    >
      {children || product.title}
    </h3>
  )
}

function ProductCardReviewStars({
  className,
  ...props
}: React.ComponentProps<'div'>) {
  const {product, reviewsDisabled} = useProductCardContext()
  const reviewAnalytics = product.reviewAnalytics

  if (reviewsDisabled || !reviewAnalytics?.averageRating) {
    return null
  }

  return (
    <div
      data-slot="product-card-review-stars"
      className={cn('', className)}
      {...props}
    >
      <ProductReviewStars
        averageRating={reviewAnalytics.averageRating}
        reviewCount={reviewAnalytics.reviewCount}
      />
    </div>
  )
}

function ProductCardPrice({className}: {className?: string}) {
  const {product, selectedProductVariant} = useProductCardContext()

  // Derive price data locally
  const displayPrice = selectedProductVariant?.price || product?.price
  const displayCompareAtPrice =
    selectedProductVariant?.compareAtPrice || product?.compareAtPrice

  return (
    <ProductVariantPrice
      amount={displayPrice?.amount || ''}
      currencyCode={displayPrice?.currencyCode || ''}
      compareAtPriceAmount={displayCompareAtPrice?.amount}
      compareAtPriceCurrencyCode={displayCompareAtPrice?.currencyCode}
      className={className}
    />
  )
}

// Special PriceOverlayBadge for price overlay variant
function ProductCardPriceOverlayBadge() {
  const {product, selectedProductVariant, variant} = useProductCardContext()
  if (variant !== 'priceOverlay') return null
  const displayPrice = selectedProductVariant?.price || product.price
  const currencyCode = displayPrice?.currencyCode
  const amount = displayPrice?.amount

  if (!currencyCode || !amount) return null
  return (
    <ProductCardBadge position="top-left">
      {formatMoney(amount, currencyCode)}
    </ProductCardBadge>
  )
}

/**
 * Optimized for grid layouts with three variants: `default`, `priceOverlay`, and `compact`. Use default for product grids where details matter, priceOverlay for visual-first feeds, compact when dealing with limited space. For some list views where space is limited, `ProductLink` may work better. Exposes composable subcomponents for custom layouts.
 * @publicDocs
 */
export interface ProductCardProps {
  /** The product to display in the card */
  product: Product
  /** Optional selected variant of the product to show specific variant data */
  selectedProductVariant?: ProductVariant
  /** Visual style variant of the card */
  variant?: 'default' | 'priceOverlay' | 'compact'
  /** Whether the card can be clicked/tapped to navigate to product details */
  touchable?: boolean
  /** Optional text to display in a badge on the card */
  badgeText?: string
  /** Visual style variant for the badge */
  badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'
  /** Callback fired when the product is clicked */
  onProductClick?: () => void
  /** Callback fired when the favorite button is toggled */
  onFavoriteToggled?: (isFavorited: boolean) => void
  /** Custom layout via children */
  children?: React.ReactNode
  /** Whether the favorite button is disabled */
  favoriteButtonDisabled?: boolean
  /** Whether review stars are disabled */
  reviewsDisabled?: boolean
  /** Whether to disable impression tracking */
  impressionTrackingDisabled?: boolean
}

function ProductCard({
  product,
  selectedProductVariant,
  variant = 'default',
  touchable = true,
  badgeText,
  badgeVariant,
  onProductClick,
  onFavoriteToggled,
  children,
  favoriteButtonDisabled = false,
  reviewsDisabled = false,
  impressionTrackingDisabled = false,
}: ProductCardProps) {
  const {navigateToProduct} = useShopNavigation()
  const {saveProduct, unsaveProduct} = useSavedProductsActions()
  const {ref: impressionRef} = useProductImpression({
    productId: product.id,
    skip: impressionTrackingDisabled,
  })

  // Local state for optimistic UI updates
  const [isFavoritedLocal, setIsFavoritedLocal] = useState(product.isFavorited)

  const handleClick = useCallback(() => {
    if (!touchable) return

    onProductClick?.()

    navigateToProduct({
      productId: product.id,
    })
  }, [navigateToProduct, product.id, touchable, onProductClick])

  const handleFavoriteClick = useCallback(async () => {
    const previousState = isFavoritedLocal

    // Optimistic update
    setIsFavoritedLocal(!previousState)
    onFavoriteToggled?.(!previousState)

    try {
      if (previousState) {
        await unsaveProduct({
          productId: product.id,
          shopId: product.shop.id,
          productVariantId:
            selectedProductVariant?.id || product.defaultVariantId,
        })
      } else {
        await saveProduct({
          productId: product.id,
          shopId: product.shop.id,
          productVariantId:
            selectedProductVariant?.id || product.defaultVariantId,
        })
      }
    } catch (error) {
      // Revert optimistic update on error
      setIsFavoritedLocal(previousState)
      onFavoriteToggled?.(previousState)
    }
  }, [
    isFavoritedLocal,
    product.id,
    product.shop.id,
    product.defaultVariantId,
    selectedProductVariant?.id,
    saveProduct,
    unsaveProduct,
    onFavoriteToggled,
  ])

  const contextValue = useMemo<ProductCardContextValue>(
    () => ({
      // Core data
      product,
      selectedProductVariant,

      // UI configuration
      variant,
      touchable,
      badgeText,
      badgeVariant,
      favoriteButtonDisabled,
      reviewsDisabled,

      // State
      isFavorited: isFavoritedLocal,
      // Actions
      onClick: handleClick,
      onFavoriteToggle: handleFavoriteClick,
    }),
    [
      product,
      selectedProductVariant,
      variant,
      touchable,
      badgeText,
      badgeVariant,
      isFavoritedLocal,
      handleClick,
      handleFavoriteClick,
      favoriteButtonDisabled,
      reviewsDisabled,
    ]
  )

  return (
    <ProductCardContext.Provider value={contextValue}>
      <div ref={impressionRef}>
        {children ?? (
          <ProductCardContainer>
            <ProductCardImageContainer>
              <ProductCardImage />
              {variant === 'priceOverlay' && <ProductCardPriceOverlayBadge />}
              <ProductCardBadge />
              <ProductCardFavoriteButton />
            </ProductCardImageContainer>
            {variant === 'default' && (
              <ProductCardInfo>
                <ProductCardTitle />
                <ProductCardReviewStars />
                <ProductCardPrice />
              </ProductCardInfo>
            )}
          </ProductCardContainer>
        )}
      </div>
    </ProductCardContext.Provider>
  )
}

export {
  ProductCard,
  ProductCardContainer,
  ProductCardImageContainer,
  ProductCardImage,
  ProductCardBadge,
  ProductCardFavoriteButton,
  ProductCardInfo,
  ProductCardTitle,
  ProductCardReviewStars,
  ProductCardPrice,
}
