import * as React from 'react'

import {type Product} from '@shopify/shop-minis-platform'
import {cva, type VariantProps} from 'class-variance-authority'
import {Slot as SlotPrimitive} from 'radix-ui'

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 {Touchable} from '../atoms/touchable'
import {Card, CardContent, CardAction} from '../ui/card'

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

const productLinkVariants = cva('', {
  variants: {
    layout: {
      horizontal: 'w-full !flex-row items-center gap-3 px-4 py-3',
      vertical: 'flex-col',
    },
    discount: {
      none: '',
      small: '',
      large: '',
    },
  },
  defaultVariants: {
    layout: 'horizontal',
    discount: 'none',
  },
})

// Primitive components (building blocks)
export interface ProductLinkRootProps
  extends
    React.ComponentProps<typeof Card>,
    VariantProps<typeof productLinkVariants> {
  layout?: 'horizontal' | 'vertical'
  asChild?: boolean
  onPress?: () => void
}

function ProductLinkRoot({
  className,
  layout,
  discount,
  asChild = false,
  onPress,
  ...props
}: ProductLinkRootProps) {
  const Comp = asChild ? SlotPrimitive.Root : Card

  return (
    <Touchable
      onClick={onPress}
      whileTap={{opacity: 0.7}}
      transition={{
        opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},
      }}
    >
      <Comp
        className={cn(
          productLinkVariants({layout, discount}),
          'border-0 bg-white rounded-xl shadow-lg shadow-black/10',
          className
        )}
        {...props}
      />
    </Touchable>
  )
}

function ProductLinkImage({
  className,
  layout = 'horizontal',
  ...props
}: React.ComponentProps<'div'> & {layout?: 'horizontal' | 'vertical'}) {
  return (
    <div
      data-slot="product-link-image"
      className={cn(
        'overflow-hidden rounded-md bg-muted',
        layout === 'horizontal'
          ? 'h-16 w-16 flex-shrink-0'
          : 'aspect-square w-full',
        className
      )}
      {...props}
    />
  )
}

function ProductLinkInfo({
  className,
  layout = 'horizontal',
  ...props
}: React.ComponentProps<typeof CardContent> & {
  layout?: 'horizontal' | 'vertical'
}) {
  return (
    <CardContent
      className={cn(
        layout === 'horizontal'
          ? 'flex-1 min-w-0 space-y-1 px-0 py-0'
          : 'space-y-2',
        className
      )}
      {...props}
    />
  )
}

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

function ProductLinkPrice({className, ...props}: React.ComponentProps<'div'>) {
  return (
    <div
      data-slot="product-link-price"
      className={cn('flex items-center gap-2', className)}
      {...props}
    />
  )
}

function ProductLinkCurrentPrice({
  className,
  ...props
}: React.ComponentProps<'span'>) {
  return (
    <span
      data-slot="product-link-current-price"
      className={cn('text-sm font-semibold text-gray-900', className)}
      {...props}
    />
  )
}

function ProductLinkOriginalPrice({
  className,
  ...props
}: React.ComponentProps<'span'>) {
  return (
    <span
      data-slot="product-link-original-price"
      className={cn('text-sm text-gray-500 line-through', className)}
      {...props}
    />
  )
}

function ProductLinkDiscountPrice({
  className,
  ...props
}: React.ComponentProps<'span'>) {
  return (
    <span
      data-slot="product-link-discount-price"
      className={cn('text-sm font-semibold text-red-600', className)}
      {...props}
    />
  )
}

function ProductLinkRating({className, ...props}: React.ComponentProps<'div'>) {
  return (
    <div
      data-slot="product-link-rating"
      className={cn('', className)}
      {...props}
    />
  )
}

function ProductLinkActions({
  className,
  hideFavoriteAction = false,
  onPress,
  filled = false,
  customAction,
  ...props
}: React.ComponentProps<typeof CardAction> & {
  hideFavoriteAction?: boolean
  onPress?: () => void
  filled?: boolean
  customAction?: React.ReactNode
}) {
  const favoriteAction = hideFavoriteAction ? null : (
    <FavoriteButton filled={filled} onClick={onPress} />
  )

  return (
    <CardAction
      className={cn('flex-shrink-0 self-center px-0 py-0', className)}
      {...props}
    >
      <Touchable
        stopPropagation
        onClick={onPress}
        whileTap={{opacity: 0.7, scale: 0.95}}
        transition={{
          opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},
          scale: {type: 'tween', duration: 0.08, ease: 'easeInOut'},
        }}
      >
        {customAction ? <>{customAction}</> : favoriteAction}
      </Touchable>
    </CardAction>
  )
}

/**
 * A list item component for displaying products in search results, lists, and feeds. `ProductLink` works well for list views and search results where vertical space is limited. For grid layouts with larger images, you can use `ProductCard` instead. Supports custom actions (e.g., "Add to Cart") in place of the favorite button.
 * @publicDocs
 */
export interface ProductLinkDocProps {
  /** The product to display */
  product: Product
  /** Hide the favorite/save button */
  hideFavoriteAction?: boolean
  /** Callback when the product link is clicked */
  onClick?: (product: Product) => void
  /** Hide the review stars */
  reviewsDisabled?: boolean
  /** Custom action element to replace the favorite button. Must be provided with `onCustomActionClick`. */
  customAction?: React.ReactNode
  /** Callback when the custom action is clicked. Must be provided with `customAction`. */
  onCustomActionClick?: () => void
  /** Whether to disable impression tracking */
  impressionTrackingDisabled?: boolean
}

export type ProductLinkProps = {
  product: Product
  hideFavoriteAction?: boolean
  onClick?: (product: Product) => void
  reviewsDisabled?: boolean
  impressionTrackingDisabled?: boolean
} & (
  | {
      customAction?: never
      onCustomActionClick?: never
    }
  | {
      customAction: React.ReactNode
      onCustomActionClick: () => void
    }
)

// Composed ProductLink component
function ProductLink({
  product,
  hideFavoriteAction = false,
  onClick,
  customAction,
  onCustomActionClick,
  reviewsDisabled = false,
  impressionTrackingDisabled = false,
}: ProductLinkProps) {
  const {navigateToProduct} = useShopNavigation()
  const {saveProduct, unsaveProduct} = useSavedProductsActions()
  const {ref: impressionRef} = useProductImpression({
    productId: product.id,
    skip: impressionTrackingDisabled,
  })

  const {
    id,
    title,
    featuredImage,
    reviewAnalytics,
    price,
    compareAtPrice,
    isFavorited,
    selectedVariant,
    defaultVariantId,
    shop,
  } = product

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

  const averageRating = reviewAnalytics?.averageRating
  const reviewCount = reviewAnalytics?.reviewCount
  const amount = price?.amount
    ? formatMoney(price?.amount, price?.currencyCode)
    : undefined
  const imageUrl = featuredImage?.url
  const imageAltText = featuredImage?.altText || title
  const compareAtPriceAmount = compareAtPrice?.amount
    ? formatMoney(compareAtPrice?.amount, compareAtPrice?.currencyCode)
    : undefined
  const hasDiscount = compareAtPriceAmount && compareAtPriceAmount !== amount

  const handlePress = React.useCallback(() => {
    navigateToProduct({
      productId: id,
    })
    onClick?.(product)
  }, [navigateToProduct, id, onClick, product])

  const handleActionPress = React.useCallback(async () => {
    if (customAction || onCustomActionClick) {
      onCustomActionClick?.()
      return
    }

    const previousState = isFavoritedLocal

    // Optimistic update
    setIsFavoritedLocal(!previousState)

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

  return (
    <div ref={impressionRef}>
      <ProductLinkRoot
        layout="horizontal"
        discount={hasDiscount ? 'small' : 'none'}
        onPress={handlePress}
      >
        <ProductLinkImage layout="horizontal">
          {imageUrl ? (
            <img
              src={imageUrl}
              alt={imageAltText}
              className="h-full w-full object-cover"
            />
          ) : (
            <div className="h-full w-full bg-muted flex items-center justify-center text-muted-foreground text-xs">
              No Image
            </div>
          )}
        </ProductLinkImage>

        <ProductLinkInfo layout="horizontal">
          <ProductLinkTitle>{title}</ProductLinkTitle>

          {averageRating && !reviewsDisabled ? (
            <ProductLinkRating>
              <ProductReviewStars
                averageRating={averageRating}
                reviewCount={reviewCount}
              />
            </ProductLinkRating>
          ) : null}

          <ProductLinkPrice>
            {hasDiscount ? (
              <>
                <ProductLinkDiscountPrice>{amount}</ProductLinkDiscountPrice>
                <ProductLinkOriginalPrice>
                  {compareAtPriceAmount}
                </ProductLinkOriginalPrice>
              </>
            ) : (
              <ProductLinkCurrentPrice>{amount}</ProductLinkCurrentPrice>
            )}
          </ProductLinkPrice>
        </ProductLinkInfo>

        <ProductLinkActions
          filled={isFavoritedLocal}
          onPress={handleActionPress}
          customAction={customAction}
          hideFavoriteAction={hideFavoriteAction}
        />
      </ProductLinkRoot>
    </div>
  )
}

export {ProductLink}
