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

import {type Shop} from '@shopify/shop-minis-platform'
import {Star} from 'lucide-react'

import {useShopNavigation} from '../../hooks/navigation/useShopNavigation'
import {cn} from '../../lib/utils'
import {
  type ExtractedBrandTheme,
  extractBrandTheme,
  formatReviewCount,
  getFeaturedImages,
  normalizeRating,
} from '../../utils'
import {isDarkColor} from '../../utils/colors'
import {Image} from '../atoms/image'
import {Touchable} from '../atoms/touchable'

interface MerchantCardContextValue {
  // Core data
  shop: Shop

  // Derived data
  cardTheme: ExtractedBrandTheme

  // UI configuration
  touchable: boolean
  featuredImagesLimit: number

  // Actions
  onClick: () => void
}

const MerchantCardContext = createContext<MerchantCardContextValue | undefined>(
  undefined
)

function useMerchantCardContext() {
  const context = useContext(MerchantCardContext)
  if (!context) {
    throw new Error(
      'useMerchantCardContext must be used within a MerchantCardProvider'
    )
  }
  return context
}

function MerchantCardContainer({
  className,
  ...props
}: React.ComponentProps<'div'>) {
  const {touchable, cardTheme, onClick} = useMerchantCardContext()

  const content = (
    <div
      style={{
        backgroundColor: cardTheme.backgroundColor,
      }}
      className={cn(
        'relative w-full overflow-hidden rounded-xl bg-white flex flex-col border border-gray-200 aspect-square',

        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 MerchantCardImage({
  className,
  src,
  alt,
  thumbhash,
  ...props
}: React.ComponentProps<'img'> & {
  src?: string
  alt?: string
  thumbhash?: string
}) {
  if (!src) {
    return <div className="w-full h-full bg-gray-100" />
  }

  if (thumbhash) {
    return (
      <Image
        data-slot="merchant-card-image"
        src={src}
        alt={alt}
        thumbhash={thumbhash}
        className={cn(className)}
        {...props}
      />
    )
  }

  return (
    <img
      data-slot="merchant-card-image"
      src={src}
      alt={alt}
      className={cn('size-full object-cover', className)}
      {...props}
    />
  )
}

function MerchantCardLogo({className, ...props}: React.ComponentProps<'div'>) {
  const {shop} = useMerchantCardContext()
  const {name, visualTheme} = shop

  const logoAverageColor = visualTheme?.brandSettings?.colors?.logoAverage
  const logoDominantColor = visualTheme?.brandSettings?.colors?.logoDominant
  const logoColor = logoAverageColor || logoDominantColor

  const logoBackgroundClassName = useMemo(
    () => (logoColor && isDarkColor(logoColor) ? 'bg-white' : 'bg-gray-800'),
    [logoColor]
  )

  const logoUrl = visualTheme?.logoImage?.url
  const altText = `${name} logo`

  return (
    <div
      data-slot="merchant-card-logo"
      className={cn(
        'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10',
        'w-16 h-16 rounded-xl bg-white border-2 border-white shadow-sm',
        'flex items-center justify-center overflow-hidden',
        logoBackgroundClassName,
        className
      )}
      {...props}
    >
      {logoUrl ? (
        <img
          src={logoUrl}
          alt={altText}
          className="w-full h-full object-cover"
        />
      ) : (
        <div className="w-full h-full bg-gray-200 flex items-center justify-center">
          <span className="text-gray-600 font-semibold text-lg">
            {name?.slice(0, 1)}
          </span>
        </div>
      )}
    </div>
  )
}

function MerchantCardInfo({className, ...props}: React.ComponentProps<'div'>) {
  const {cardTheme} = useMerchantCardContext()

  const isDarkTheme = useMemo(() => {
    return (
      cardTheme.backgroundColor !== 'white' &&
      isDarkColor(cardTheme.backgroundColor)
    )
  }, [cardTheme.backgroundColor])

  const textColor = isDarkTheme ? 'text-primary-foreground' : 'text-foreground'

  return (
    <div
      data-slot="merchant-card-info"
      className={cn(
        'p-3 flex-shrink-0 flex flex-col min-w-0',
        textColor,
        className
      )}
      {...props}
    />
  )
}

function MerchantCardName({
  className,
  children,
  ...props
}: React.ComponentProps<'h3'>) {
  const {shop} = useMerchantCardContext()
  const {name} = shop
  const nameContent = children ?? name

  return (
    <h3
      data-slot="merchant-card-name"
      className={cn('text-sm font-medium truncate', className)}
      {...props}
    >
      {nameContent}
    </h3>
  )
}

function MerchantCardRating({
  className,

  ...props
}: React.ComponentProps<'div'> & {
  rating?: number | null
  reviewCount?: number
}) {
  const {shop} = useMerchantCardContext()

  const {
    reviewAnalytics: {averageRating, reviewCount},
  } = shop

  if (!averageRating || !reviewCount) return null

  return (
    <div
      data-slot="merchant-card-rating"
      className={cn('flex items-center gap-1 text-xs', className)}
      {...props}
    >
      <Star className="h-3 w-3 fill-current" />
      <span className="text-xs">
        {normalizeRating(averageRating)} ({formatReviewCount(reviewCount)})
      </span>
    </div>
  )
}

function MerchantCardDefaultHeader({withLogo = false}: {withLogo?: boolean}) {
  const {shop, cardTheme, featuredImagesLimit} = useMerchantCardContext()
  const {visualTheme} = shop

  const featuredImages = useMemo(
    () => getFeaturedImages(visualTheme, featuredImagesLimit),
    [visualTheme, featuredImagesLimit]
  )

  const numberOfFeaturedImages = featuredImages?.length ?? 0

  const displayDefaultCover = () => {
    if (numberOfFeaturedImages > 0) {
      const heightClass = numberOfFeaturedImages === 2 ? 'h-full' : 'h-1/2'
      return featuredImages?.map((image, index) => (
        <div className={`z-0 w-1/2 ${heightClass}`} key={image.url || index}>
          <MerchantCardImage
            src={image.url}
            alt={image.altText ?? undefined}
            thumbhash={image.thumbhash ?? undefined}
            className="aspect-square"
          />
        </div>
      ))
    } else if (cardTheme.type === 'coverImage') {
      return (
        <MerchantCardImage
          src={cardTheme.coverImageUrl}
          thumbhash={cardTheme.coverImageThumbhash}
        />
      )
    }

    return null
  }

  return (
    <div className="w-full h-full bg-muted relative flex flex-wrap overflow-hidden">
      {withLogo && (
        <div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10">
          <MerchantCardLogo />
        </div>
      )}

      {displayDefaultCover()}
    </div>
  )
}

function MerchantCardBrandedHeader({withLogo = false}: {withLogo?: boolean}) {
  const {shop, cardTheme} = useMerchantCardContext()
  const wordmarkImage = shop.visualTheme?.brandSettings?.headerTheme?.wordmark

  return (
    <div className="size-full relative">
      {cardTheme.type === 'coverImage' && (
        <>
          <MerchantCardImage
            src={cardTheme.coverImageUrl}
            alt={shop.name}
            thumbhash={cardTheme.coverImageThumbhash ?? undefined}
          />

          <div className="absolute inset-0 z-[1] bg-black/20" />

          <div
            className="absolute bottom-0 z-[1] size-full"
            style={{
              background: `linear-gradient(to top, ${cardTheme.backgroundColor} 0%, ${cardTheme.backgroundColor}00 40%)`,
            }}
          />
        </>
      )}

      {withLogo && (
        <div className="absolute inset-0 z-[1] flex items-center justify-center">
          {wordmarkImage ? (
            <img
              src={wordmarkImage.url}
              alt={wordmarkImage.altText || shop.name}
              className="max-h-16 min-h-10 max-w-28 object-contain"
              data-testid="store-data-wordmark"
            />
          ) : (
            <MerchantCardLogo />
          )}
        </div>
      )}
    </div>
  )
}

interface MerchantCardHeaderProps {
  isDefault?: boolean
  withLogo?: boolean
}

function MerchantCardHeader({
  isDefault,
  withLogo,
  className,
  ...props
}: React.ComponentProps<'div'> & MerchantCardHeaderProps) {
  const {cardTheme} = useMerchantCardContext()

  const isBranded =
    cardTheme.type === 'coverImage' || cardTheme.type === 'brandColor'

  return (
    <div
      className={cn('relative overflow-hidden flex-1 flex-wrap', className)}
      {...props}
    >
      {isBranded && !isDefault ? (
        <MerchantCardBrandedHeader withLogo={withLogo} />
      ) : (
        <MerchantCardDefaultHeader withLogo={withLogo} />
      )}
    </div>
  )
}

export interface MerchantCardProps {
  shop: Shop
  touchable?: boolean
  featuredImagesLimit?: number
  children?: React.ReactNode
}

function MerchantCard({
  shop,
  touchable = true,
  featuredImagesLimit = 4,
  children,
}: MerchantCardProps) {
  const {navigateToShop} = useShopNavigation()

  const {id, visualTheme} = shop

  const handleClick = useCallback(() => {
    if (!touchable) return
    navigateToShop({shopId: id})
  }, [navigateToShop, id, touchable])

  const cardTheme = useMemo(
    () => extractBrandTheme(visualTheme?.brandSettings),
    [visualTheme?.brandSettings]
  )

  const contextValue = useMemo<MerchantCardContextValue>(
    () => ({
      shop,
      cardTheme,
      touchable,
      featuredImagesLimit,
      onClick: handleClick,
    }),
    [shop, cardTheme, touchable, featuredImagesLimit, handleClick]
  )

  return (
    <MerchantCardContext.Provider value={contextValue}>
      {children ?? (
        <MerchantCardContainer>
          <MerchantCardHeader withLogo />
          <MerchantCardInfo>
            <MerchantCardName />
            <MerchantCardRating />
          </MerchantCardInfo>
        </MerchantCardContainer>
      )}
    </MerchantCardContext.Provider>
  )
}

export {
  MerchantCard,
  MerchantCardContainer,
  MerchantCardHeader,
  MerchantCardInfo,
  MerchantCardName,
  MerchantCardRating,
}
