import { AnimatePresence, motion, useReducedMotion } from 'motion/react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import broadcast from '@/assets/system/broadcast.webp'
import { JackpotPoolTicker } from '@/components/jackpot-pool-ticker.tsx'
import { SmartImage } from '@/components/smart-image.tsx'
import { cn } from '@/lib/utils.ts'
import { useGameSessionStore } from '@/store/game'
import type { JackpotBroadcastItem } from '@/type'

const winAmountFormatter = new Intl.NumberFormat('en-US', {
  maximumFractionDigits: 6,
})
const MESSAGE_BROADCAST_ANIMATION_MS = 28_000

function formatWinAmount(value: string) {
  const amount = Number(value)

  return Number.isFinite(amount) ? winAmountFormatter.format(amount) : value
}

type MessageBroadcastProps = {
  className?: string
  showJackpotPool?: boolean
}

export function MessageBroadcast({
  className,
  showJackpotPool = true,
}: MessageBroadcastProps) {
  const { t } = useTranslation()
  const prefersReducedMotion = useReducedMotion()
  const jackpotBroadcasts = useGameSessionStore(
    (state) => state.jackpotBroadcasts,
  )
  const shownBroadcastIdsRef = useRef(new Set<string>())
  const queuedBroadcastIdsRef = useRef(new Set<string>())
  const [queue, setQueue] = useState<JackpotBroadcastItem[]>([])
  const [activeBroadcast, setActiveBroadcast] =
    useState<JackpotBroadcastItem | null>(null)
  const activeBroadcastLabel = useMemo(() => {
    if (!activeBroadcast) {
      return ''
    }

    return t('game.jackpotBroadcast.ariaMessage', {
      amount: formatWinAmount(activeBroadcast.totalWin),
      nickname: activeBroadcast.nickname,
      streak: activeBroadcast.currentStreak,
    })
  }, [activeBroadcast, t])

  useEffect(() => {
    const nextBroadcasts = jackpotBroadcasts.filter((item) => {
      if (
        shownBroadcastIdsRef.current.has(item.id) ||
        queuedBroadcastIdsRef.current.has(item.id) ||
        activeBroadcast?.id === item.id
      ) {
        return false
      }

      return true
    })

    if (nextBroadcasts.length === 0) {
      return
    }

    for (const item of nextBroadcasts) {
      queuedBroadcastIdsRef.current.add(item.id)
    }

    setQueue((currentQueue) => [...currentQueue, ...nextBroadcasts])
  }, [activeBroadcast?.id, jackpotBroadcasts])

  useEffect(() => {
    if (activeBroadcast || queue.length === 0) {
      return
    }

    const [nextBroadcast, ...remainingQueue] = queue

    queuedBroadcastIdsRef.current.delete(nextBroadcast.id)
    shownBroadcastIdsRef.current.add(nextBroadcast.id)
    setActiveBroadcast(nextBroadcast)
    setQueue(remainingQueue)
  }, [activeBroadcast, queue])

  useEffect(() => {
    if (!activeBroadcast) {
      return
    }

    const timerId = window.setTimeout(() => {
      setActiveBroadcast((currentBroadcast) =>
        currentBroadcast?.id === activeBroadcast.id ? null : currentBroadcast,
      )
    }, MESSAGE_BROADCAST_ANIMATION_MS)

    return () => {
      window.clearTimeout(timerId)
    }
  }, [activeBroadcast])

  return (
    <section
      aria-label={t('game.jackpotBroadcast.ariaLabel')}
      className={cn(
        'common-neon-inset flex w-full min-w-0 items-center overflow-hidden',
        'h-design-24 gap-design-4 !rounded-[3px] !px-design-5 !py-0 text-design-8',
        'md:h-design-65 md:gap-design-12 md:!rounded-[5px] md:!px-design-20 md:text-design-16',
        className,
      )}
    >
      <div className="flex min-w-0 flex-1 items-center gap-design-4 md:gap-design-10">
        <SmartImage
          className="h-design-12 w-design-12 shrink-0 md:h-design-24 md:w-design-24"
          alt={t('game.jackpotBroadcast.iconAlt')}
          src={broadcast}
        />
        <div className="relative h-design-16 min-w-0 flex-1 overflow-hidden md:h-design-28">
          <AnimatePresence>
            {activeBroadcast ? (
              <motion.div
                aria-label={activeBroadcastLabel}
                className="absolute inset-0 flex min-w-max items-center whitespace-nowrap font-semibold"
                key={activeBroadcast.id}
                initial={
                  prefersReducedMotion
                    ? { opacity: 0 }
                    : { opacity: 1, x: '108%' }
                }
                animate={
                  prefersReducedMotion
                    ? { opacity: 1 }
                    : { opacity: 1, x: '-108%' }
                }
                exit={{ opacity: 0 }}
                transition={{
                  duration: prefersReducedMotion
                    ? 0.18
                    : MESSAGE_BROADCAST_ANIMATION_MS / 1000,
                  ease: prefersReducedMotion ? [0.16, 1, 0.3, 1] : 'linear',
                }}
              >
                <span className="text-[#D5F7FF]">
                  {t('game.jackpotBroadcast.prefix')}
                </span>
                <span className="ml-design-4 text-[#4BFFFE] md:ml-design-8">
                  {activeBroadcast.nickname}
                </span>
                <span className="mx-design-4 text-[#D5F7FF] md:mx-design-8">
                  {t('game.jackpotBroadcast.separator')}
                </span>
                <span className="text-[#78FF7F]">
                  {t('game.jackpotBroadcast.streak', {
                    count: activeBroadcast.currentStreak,
                  })}
                </span>
                <span className="mx-design-4 text-[#D5F7FF] md:mx-design-8">
                  {t('game.jackpotBroadcast.winAction')}
                </span>
                <span className="text-[#FFB72B]">
                  {formatWinAmount(activeBroadcast.totalWin)}
                </span>
              </motion.div>
            ) : null}
          </AnimatePresence>
        </div>
      </div>
      {showJackpotPool ? (
        <JackpotPoolTicker
          variant="inline"
          className="!w-fit max-w-[54%] shrink-0 md:max-w-[calc(var(--design-unit)*320)]"
        />
      ) : null}
    </section>
  )
}
