import { useEffect, useRef, useState } from 'react'
import { Play, Heart, Check, Library } from '../../lib/icons'
import { motion, useMotionValue, useSpring, useTransform } from 'framer-motion'
import type { BaseItemDto } from '../../api/types'
import { getBestImage, getStoredServerUrl } from '../../api/jellyfin'
import MetaBadges from './MetaBadges'
import { usePreferencesStore } from '../../stores/preferences-store'
import { useHoverTrailer } from '../../hooks/use-hover-trailer'
import { useCollectionMeter } from '../../hooks/use-collection-meter'
import { usePrebuffer } from '../../hooks/use-prebuffer'
import { useQuickLookStore } from '../../stores/quick-look-store'
import AvailabilityChip from './AvailabilityChip'
import { tmdbDecoration, isInLibrary, getTmdbId } from '../../lib/item-types'

interface Props {
  item: BaseItemDto
  aspect?: 'poster' | 'landscape' | 'square'
  onClick?: () => void
  priority?: boolean
  /** Mark this card as belonging to the user's local library (TMDB id matched). */
  inLibrary?: boolean
  /** Reduce visual weight for items NOT in library (in mixed grids). */
  dim?: boolean
  /** Multi-select state from the library selection store. When `selected` is
   *  true the card renders a check overlay; the parent owns the toggle. */
  selected?: boolean
  /** Called on Ctrl/Cmd-click when selection is enabled. Returns true if
   *  the click should be considered a selection toggle (suppressing
   *  navigation). */
  onToggleSelect?: () => void
  /** Library context: rendered as part of the user's own library page.
   *  Suppresses badges that are redundant in that context (no point
   *  flagging "Available" or "In library" on items the user obviously
   *  already owns). */
  libraryContext?: boolean
  /** Tighter corner radius for dense layouts (Library "map" view) where
   *  rounded-lg crops too much of the artwork at small card sizes. */
  compact?: boolean
}

export default function PosterCard({
  item,
  aspect = 'poster',
  onClick,
  priority = false,
  inLibrary = false,
  dim = false,
  selected = false,
  onToggleSelect,
  libraryContext = false,
  compact = false,
}: Props) {
  const cardRef = useRef<HTMLButtonElement>(null)
  const showTechBadges = usePreferencesStore(s => s.showTechBadges)
  const reduceMotion = usePreferencesStore(s => s.reduceMotion)
  const hoverTrailersPref = usePreferencesStore(s => s.hoverTrailers)
  const quickLookPref = usePreferencesStore(s => s.quickLookEnabled)
  const openQuickLook = useQuickLookStore(s => s.open)
  const [hovered, setHovered] = useState(false)
  const [imgLoaded, setImgLoaded] = useState(false)
  const [imgError, setImgError] = useState(false)
  const [trailerStarted, setTrailerStarted] = useState(false)
  const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
  const trailerUnmountTimer = useRef<ReturnType<typeof setTimeout> | null>(null)

  const trailer = useHoverTrailer(item, hovered, hoverTrailersPref && !reduceMotion)
  // Same hover-arm gate as the trailer: meter only fetches once the user
  // has paused on the card. Returns null when the movie isn't part of a
  // TMDB collection.
  const collectionMeterPref = usePreferencesStore(s => s.detail.show.collectionMeter)
  const collectionMeter = useCollectionMeter(item, hovered && collectionMeterPref)
  // Pre-fire PlaybackInfo + first-kilobyte fetch so the player click→play
  // path feels instant. Hover-armed for the same reason as the trailer:
  // we don't want to slam the server when sweeping the cursor across a
  // grid.
  const prebufferPref = usePreferencesStore(s => s.prebufferOnHover)
  usePrebuffer(item, hovered && prebufferPref)
  // Mount the trailer iframe when we have a video AND we're hovered.
  // Unmount is delayed by 1.5s after leaving so a brief mouse-out
  // (moving to read the title, sliding past, etc.) doesn't tear down
  // the YouTube load and re-trigger autoplay on the next hover.
  useEffect(() => {
    if (hovered && trailer.videoKey) {
      if (trailerUnmountTimer.current) {
        clearTimeout(trailerUnmountTimer.current)
        trailerUnmountTimer.current = null
      }
      if (!trailerStarted) setTrailerStarted(true)
      return
    }
    if (!hovered && trailerStarted) {
      if (trailerUnmountTimer.current) clearTimeout(trailerUnmountTimer.current)
      trailerUnmountTimer.current = setTimeout(() => setTrailerStarted(false), 1500)
      return () => {
        if (trailerUnmountTimer.current) {
          clearTimeout(trailerUnmountTimer.current)
          trailerUnmountTimer.current = null
        }
      }
    }
    return undefined
  }, [hovered, trailer.videoKey, trailerStarted])

  // Magnetic tilt - flat rotation when reduce-motion is enabled
  const mx = useMotionValue(0)
  const my = useMotionValue(0)
  const rotateX = useSpring(useTransform(my, [-0.5, 0.5], reduceMotion ? [0, 0] : [3.5, -3.5]), { stiffness: 220, damping: 22 })
  const rotateY = useSpring(useTransform(mx, [-0.5, 0.5], reduceMotion ? [0, 0] : [-3.5, 3.5]), { stiffness: 220, damping: 22 })
  const glareX = useTransform(mx, [-0.5, 0.5], ['25%', '75%'])
  const glareY = useTransform(my, [-0.5, 0.5], ['25%', '75%'])

  function handleMove(e: React.MouseEvent<HTMLButtonElement>) {
    if (reduceMotion || !cardRef.current) return
    const rect = cardRef.current.getBoundingClientRect()
    mx.set((e.clientX - rect.left) / rect.width - 0.5)
    my.set((e.clientY - rect.top) / rect.height - 0.5)
  }
  function handleLeave() {
    setHovered(false)
    // trailerStarted is reset by the delayed-unmount effect above so a
    // brief cursor-out keeps the iframe alive.
    if (longPressTimer.current) {
      clearTimeout(longPressTimer.current)
      longPressTimer.current = null
    }
    mx.set(0)
    my.set(0)
  }

  function handleContextMenu(e: React.MouseEvent) {
    if (!quickLookPref) return
    e.preventDefault()
    openQuickLook(item)
  }
  function handleTouchStart() {
    if (!quickLookPref) return
    if (longPressTimer.current) clearTimeout(longPressTimer.current)
    longPressTimer.current = setTimeout(() => openQuickLook(item), 550)
  }
  function handleTouchEnd() {
    if (longPressTimer.current) {
      clearTimeout(longPressTimer.current)
      longPressTimer.current = null
    }
  }

  const serverUrl = getStoredServerUrl()

  const tmdbPosterUrl = tmdbDecoration(item)._tmdbPoster
  const imageUrl =
    getBestImage(
      serverUrl,
      item,
      aspect === 'landscape' ? 'thumb' : 'primary',
      aspect === 'poster' ? 320 : 480,
    ) ||
    tmdbPosterUrl ||
    ''
  // Items can carry `_inLibrary` from a TMDB-mapped synthetic; respect it as default.
  const effectiveInLibrary = inLibrary || isInLibrary(item)

  const title = item.Name || item.SeriesName || 'Untitled'
  const subtitle = [
    item.ProductionYear,
    item.RunTimeTicks ? `${Math.round(Number(item.RunTimeTicks) / 600000000)} min` : '',
    item.Type === 'Episode' && item.ParentIndexNumber != null && item.IndexNumber != null
      ? `S${item.ParentIndexNumber} - E${item.IndexNumber}`
      : '',
  ].filter(Boolean).join(' · ')

  const progress = item.UserData?.PlayedPercentage
  const isWatched = item.UserData?.Played
  const isFavorite = item.UserData?.IsFavorite

  const aspectClass =
    aspect === 'poster' ? 'aspect-[2/3]'
    : aspect === 'square' ? 'aspect-square'
    : 'aspect-video'

  // Iframes don't honor `object-fit: cover`. We compute explicit
  // cover dimensions from the card aspect AND oversize the iframe by
  // ~30% in each axis so the edge chrome (top title bar, bottom
  // control bar) falls outside the visible window. The video stays
  // centered via translate(-50%, -50%) and the parent's overflow:hidden
  // crops the bleed.
  //
  // YouTube's newer player paints a big center play/prev/next overlay
  // we can't crop around anyway, so there's no point oversizing
  // further at the cost of seeing less of the actual trailer. 30%
  // bleed catches the edge chrome and lets the trailer composition
  // stay mostly intact.
  //
  // height = 130% of card height (15% bleed above + below)
  // width  = height × 16/9 × (card_height / card_width)
  // poster    (h/w 1.5):    width ≈ 346.67%, height 130%
  // square    (h/w 1):      width ≈ 231.11%, height 130%
  // landscape (h/w 0.5625): width = 130%,    height 130%
  const trailerCoverStyle: React.CSSProperties =
    aspect === 'poster'
      ? { width: '346.67%', height: '130%' }
      : aspect === 'square'
        ? { width: '231.11%', height: '130%' }
        : { width: '130%', height: '130%' }

  return (
    <motion.button
      ref={cardRef}
      onClick={(e) => {
        // Ctrl/Cmd-click is a selection toggle, not a navigation. The
        // parent decides whether to honor it via onToggleSelect.
        if (onToggleSelect && (e.ctrlKey || e.metaKey || e.shiftKey)) {
          e.preventDefault()
          e.stopPropagation()
          onToggleSelect()
          return
        }
        // When something is already selected, plain clicks toggle as well
        // so the user can fluidly grow / shrink the selection.
        if (onToggleSelect && selected) {
          e.preventDefault()
          e.stopPropagation()
          onToggleSelect()
          return
        }
        onClick?.()
      }}
      onMouseMove={handleMove}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={handleLeave}
      onContextMenu={handleContextMenu}
      onTouchStart={handleTouchStart}
      onTouchEnd={handleTouchEnd}
      onTouchMove={handleTouchEnd}
      style={{ rotateX, rotateY, transformStyle: 'preserve-3d', transformPerspective: 1000 }}
      whileTap={{ scale: 0.97 }}
      transition={{ duration: 0.18, ease: [0.16, 1, 0.3, 1] }}
      className={`group text-left w-full focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-void ${compact ? 'rounded-[3px]' : 'rounded-lg'}`}
    >
      <div
        className={`relative overflow-hidden ${compact ? 'rounded-[3px]' : 'rounded-lg'} bg-elevated/60 transition-all duration-200 ${aspectClass} ${
          selected
            ? 'ring-2 ring-accent shadow-[0_0_24px_-2px_rgba(245,182,66,0.7)]'
            : effectiveInLibrary
              ? 'ring-2 ring-accent/60 shadow-[0_0_24px_-6px_rgba(245,182,66,0.45)]'
              : 'ring-1 ring-border'
        } ${dim && !effectiveInLibrary ? 'opacity-65 grayscale-[20%] hover:opacity-100 hover:grayscale-0' : ''}`}
        style={{ transform: 'translateZ(0)' }}
      >
        {/* Skeleton */}
        {!imgLoaded && !imgError && <div className="absolute inset-0 skeleton" />}

        {/* Image */}
        {imageUrl && !imgError && (
          <motion.img
            src={imageUrl}
            alt={title}
            loading={priority ? 'eager' : 'lazy'}
            decoding="async"
            onLoad={() => setImgLoaded(true)}
            onError={() => setImgError(true)}
            initial={{ opacity: 0, scale: 1.02 }}
            animate={{
              opacity: imgLoaded ? 1 : 0,
              scale: hovered ? 1.08 : 1,
            }}
            transition={{
              opacity: { duration: 0.45, ease: [0.16, 1, 0.3, 1] },
              scale: { duration: 0.7, ease: [0.16, 1, 0.3, 1] },
            }}
            className="absolute inset-0 w-full h-full object-cover"
          />
        )}

        {/* Image fallback (no image) */}
        {(!imageUrl || imgError) && (
          <div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-elevated to-surface">
            <span className="text-text-4 text-[36px] font-display font-semibold opacity-50 select-none">
              {title[0]?.toUpperCase()}
            </span>
          </div>
        )}

        {/* Hover trailer - YouTube embed with autoplay + mute. Sized to
            COVER the card (not contain): the iframe is wider than the
            parent for poster/square aspects so the 16:9 video fully
            fills the visible frame and the sides crop. All YouTube
            chrome is masked via player params + opaque overlays so the
            user sees motion art, not a player. */}
        {trailerStarted && trailer.videoKey && (
          <div className="absolute inset-0 overflow-hidden pointer-events-none bg-black transition-opacity duration-300">
            <iframe
              src={`https://www.youtube-nocookie.com/embed/${trailer.videoKey}?autoplay=1&mute=1&controls=0&modestbranding=1&playsinline=1&rel=0&loop=1&playlist=${trailer.videoKey}&iv_load_policy=3&disablekb=1&fs=0&showinfo=0&cc_load_policy=0&color=white`}
              title=""
              allow="autoplay; encrypted-media; picture-in-picture"
              referrerPolicy="strict-origin-when-cross-origin"
              className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 border-0 pointer-events-none"
              style={trailerCoverStyle}
            />
          </div>
        )}

        {/* Bottom gradient (always there for legibility, intensifies on hover) */}
        <div className="absolute inset-x-0 bottom-0 h-2/3 bg-gradient-to-t from-black/85 via-black/30 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />

        {/* Tinted vignette on hover */}
        <div
          className="absolute inset-0 transition-opacity duration-300"
          style={{ opacity: hovered ? 1 : 0, boxShadow: 'inset 0 0 60px rgba(0,0,0,0.5)' }}
        />

        {/* Diagonal glare following cursor */}
        <motion.div
          className="absolute inset-0 pointer-events-none mix-blend-overlay"
          style={{
            opacity: hovered ? 0.5 : 0,
            background: `radial-gradient(circle at ${glareX.get()}% ${glareY.get()}%, rgba(255,255,255,0.35), transparent 45%)`,
            transition: 'opacity 200ms ease-out',
          }}
        />

        {/* Play affordance */}
        <motion.div
          className="absolute inset-0 grid place-items-center"
          initial={false}
          animate={{
            opacity: hovered ? 1 : 0,
            scale: hovered ? 1 : 0.7,
          }}
          transition={{ type: 'spring', stiffness: 400, damping: 24 }}
        >
          <div className="relative">
            <div className="absolute inset-0 rounded-full bg-accent blur-xl opacity-50" />
            <div className="relative w-12 h-12 rounded-full bg-accent grid place-items-center shadow-lg shadow-black/50 ring-1 ring-accent-hover">
              <Play size={20} className="text-void translate-x-0.5" fill="currentColor" />
            </div>
          </div>
        </motion.div>

        {/* Top-left status row */}
        <div className="absolute top-2 left-2 flex items-center gap-1">
          {selected && (
            <div className="w-6 h-6 rounded-full bg-accent text-void grid place-items-center shadow-md">
              <Check size={13} strokeWidth={3} />
            </div>
          )}
          {!selected && !libraryContext && effectiveInLibrary && (
            <div className="inline-flex items-center gap-1 h-[18px] px-1.5 rounded bg-accent/95 text-void text-[9px] font-bold uppercase tracking-[0.04em] backdrop-blur shadow-md">
              <Library size={9} strokeWidth={2.5} />
              In library
            </div>
          )}
          {!selected && !libraryContext && !effectiveInLibrary && (
            <AvailabilityChip
              tmdbId={getTmdbId(item)}
            />
          )}
          {isFavorite && (
            <div className="w-5 h-5 rounded-full bg-black/50 backdrop-blur grid place-items-center">
              <Heart size={10} className="text-accent" fill="currentColor" />
            </div>
          )}
        </div>

        {/* Top-right status row + tech badges */}
        <div className="absolute top-2 right-2 flex flex-col items-end gap-1.5">
          {isWatched && (
            <div className="w-5 h-5 rounded-full bg-accent grid place-items-center">
              <Check size={10} className="text-void" strokeWidth={3} />
            </div>
          )}
          {aspect !== 'square' && showTechBadges && <MetaBadges item={item} />}
        </div>


        {/* Hover meta strip - title floats up */}
        <motion.div
          initial={false}
          animate={{ opacity: hovered ? 1 : 0, y: hovered ? 0 : 8 }}
          transition={{ duration: 0.25, ease: [0.16, 1, 0.3, 1] }}
          className="absolute inset-x-0 bottom-0 px-3 pb-2.5 pt-8"
        >
          <p className="text-[12px] text-white font-semibold drop-shadow line-clamp-2 leading-tight">
            {title}
          </p>
          {subtitle && (
            <p className="text-[10px] text-white/70 drop-shadow mt-0.5 truncate">
              {subtitle}
            </p>
          )}
        </motion.div>

        {/* Collection completion meter - shown on hover when this movie
            is part of a TMDB collection. Sits above the play-progress
            bar so they don't fight for the same pixels. */}
        {hovered && collectionMeter && (
          <motion.div
            initial={{ opacity: 0, y: 4 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.22, ease: [0.16, 1, 0.3, 1] }}
            className="absolute left-2 right-2 bottom-2 px-2 py-1.5 rounded-md bg-black/65 backdrop-blur-sm ring-1 ring-white/15"
          >
            <div className="flex items-center justify-between text-[9.5px] uppercase tracking-[0.12em] font-semibold text-white/85 mb-1">
              <span className="truncate">{collectionMeter.collectionName}</span>
              <span className="tabular-nums shrink-0 ml-1.5">
                {collectionMeter.watched}/{collectionMeter.total}
              </span>
            </div>
            <div className="h-1 rounded-full bg-white/15 overflow-hidden">
              <div
                className="h-full bg-accent transition-[width] duration-300"
                style={{ width: `${(collectionMeter.watched / collectionMeter.total) * 100}%` }}
              />
            </div>
          </motion.div>
        )}

        {/* Progress bar - flush bottom, accent fill */}
        {progress != null && progress > 0 && !isWatched && (
          <div className="absolute bottom-0 left-0 right-0 h-[3px] bg-black/40">
            <motion.div
              initial={{ width: 0 }}
              animate={{ width: `${progress}%` }}
              transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
              className="h-full bg-accent shadow-[0_0_8px_rgba(245,182,66,0.6)]"
            />
          </div>
        )}
      </div>

      {/* Below text */}
      <div className="mt-2.5 px-px">
        <p className={`text-[13.5px] truncate leading-snug tracking-[-0.01em] ${
          effectiveInLibrary ? 'text-text-1 font-semibold' : 'text-text-1 font-medium'
        }`}>
          {title}
        </p>
        {subtitle && (
          <p className="text-[11.5px] text-text-3 truncate mt-1 leading-tight tabular-nums">
            {subtitle}
          </p>
        )}
      </div>
    </motion.button>
  )
}
