import React, { useEffect, useMemo, useRef, useState } from 'react'
import { usePage } from '@inertiajs/react'
import LevelBadge from '../xp/LevelBadge'

const IMAGE_FALLBACK = 'https://files.skinbase.org/default/missing_md.webp'
const AVATAR_FALLBACK = 'https://files.skinbase.org/default/avatar_default.webp'
const numberFormatter = new Intl.NumberFormat('en-US', {
  notation: 'compact',
  maximumFractionDigits: 1,
})
const relativeTimeFormatter = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })

function cx(...parts) {
  return parts.filter(Boolean).join(' ')
}

function formatCount(value) {
  const numeric = Number(value ?? 0)
  if (!Number.isFinite(numeric)) return '0'
  return numberFormatter.format(numeric)
}

function formatRelativeTime(value) {
  if (!value) return ''

  const date = value instanceof Date ? value : new Date(value)
  if (Number.isNaN(date.getTime())) return ''

  const now = new Date()
  const diffMs = date.getTime() - now.getTime()
  const diffSeconds = Math.round(diffMs / 1000)
  const absSeconds = Math.abs(diffSeconds)

  if (absSeconds < 60) return relativeTimeFormatter.format(diffSeconds, 'second')

  const diffMinutes = Math.round(diffSeconds / 60)
  if (Math.abs(diffMinutes) < 60) return relativeTimeFormatter.format(diffMinutes, 'minute')

  const diffHours = Math.round(diffSeconds / 3600)
  if (Math.abs(diffHours) < 24) return relativeTimeFormatter.format(diffHours, 'hour')

  const diffDays = Math.round(diffSeconds / 86400)
  if (Math.abs(diffDays) < 7) return relativeTimeFormatter.format(diffDays, 'day')

  const diffWeeks = Math.round(diffSeconds / 604800)
  if (Math.abs(diffWeeks) < 5) return relativeTimeFormatter.format(diffWeeks, 'week')

  const diffMonths = Math.round(diffSeconds / 2629800)
  if (Math.abs(diffMonths) < 12) return relativeTimeFormatter.format(diffMonths, 'month')

  const diffYears = Math.round(diffSeconds / 31557600)
  return relativeTimeFormatter.format(diffYears, 'year')
}

function slugify(value) {
  return String(value ?? '')
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-|-$/g, '')
}

function decodeHtml(value) {
  const text = String(value ?? '')
  if (!text.includes('&')) return text

  let decoded = text

  for (let index = 0; index < 3; index += 1) {
    decoded = decoded
      .replace(/&amp;/gi, '&')
      .replace(/&(apos|#39);/gi, "'")
      .replace(/&(acute|#180|#x00B4);/gi, "'")
      .replace(/&(quot|#34);/gi, '"')
      .replace(/&(nbsp|#160);/gi, ' ')

    if (typeof document === 'undefined') {
      break
    }

    const textarea = document.createElement('textarea')
    textarea.innerHTML = decoded
    const nextValue = textarea.value
    if (nextValue === decoded) break
    decoded = nextValue
  }

  return decoded
}

function normalizeContentTypeLabel(value) {
  const raw = decodeHtml(value).trim()
  if (!raw) return ''

  const normalized = raw.toLowerCase()
  const knownLabels = {
    artworks: 'Artwork',
    artwork: 'Artwork',
    wallpapers: 'Wallpaper',
    wallpaper: 'Wallpaper',
    skins: 'Skin',
    skin: 'Skin',
    photography: 'Photography',
    photo: 'Photography',
    photos: 'Photography',
    other: 'Other',
  }

  if (knownLabels[normalized]) {
    return knownLabels[normalized]
  }

  return raw
    .replace(/[-_]+/g, ' ')
    .replace(/\b\w/g, (char) => char.toUpperCase())
}

function getCsrfToken() {
  if (typeof document === 'undefined') return ''
  return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || ''
}

function swapImageToFallbackOnce(event, fallbackSrc, { clearResponsive = false } = {}) {
  const image = event.currentTarget

  if (!image || image.dataset.fallbackApplied === '1') {
    return
  }

  image.dataset.fallbackApplied = '1'
  image.onerror = null

  if (clearResponsive) {
    image.removeAttribute('srcset')
    image.removeAttribute('sizes')
  }

  image.src = fallbackSrc
}

function sendDiscoveryEvent(endpoint, payload) {
  if (!endpoint) return

  void fetch(endpoint, {
    method: 'POST',
    credentials: 'same-origin',
    keepalive: true,
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-TOKEN': getCsrfToken(),
      'X-Requested-With': 'XMLHttpRequest',
    },
    body: JSON.stringify(payload),
  }).catch(() => {})
}

async function sendFeedbackSignal(endpoint, payload) {
  if (!endpoint) {
    throw new Error('missing_feedback_endpoint')
  }

  const response = await fetch(endpoint, {
    method: 'POST',
    credentials: 'same-origin',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-TOKEN': getCsrfToken(),
      'X-Requested-With': 'XMLHttpRequest',
    },
    body: JSON.stringify(payload),
  })

  if (!response.ok) {
    throw new Error('feedback_request_failed')
  }

  return response.json().catch(() => null)
}

async function requestJson(endpoint, { method = 'GET', body } = {}) {
  const response = await fetch(endpoint, {
    method,
    credentials: 'same-origin',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
      'X-CSRF-TOKEN': getCsrfToken(),
      'X-Requested-With': 'XMLHttpRequest',
    },
    body: body ? JSON.stringify(body) : undefined,
  })

  const payload = await response.json().catch(() => ({}))
  if (!response.ok) {
    const error = new Error(payload?.message || 'Request failed.')
    error.payload = payload
    throw error
  }

  return payload
}

function trackRecommendationFeedback(item, eventType, extraMeta = {}) {
  const endpoint = item?.discovery_endpoint
  const artworkId = Number(item?.id ?? 0)
  if (!endpoint || artworkId <= 0) return

  sendDiscoveryEvent(endpoint, {
    event_type: eventType,
    artwork_id: artworkId,
    algo_version: item?.recommendation_algo_version || item?.algo_version || undefined,
    meta: {
      gallery_type: item?.recommendation_surface || item?.gallery_type || 'recommendation-surface',
      source: item?.recommendation_source || null,
      reason: item?.recommendation_reason || null,
      score: item?.recommendation_score ?? null,
      ...extraMeta,
    },
  })
}

function HeartIcon(props) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" aria-hidden="true" {...props}>
      <path strokeLinecap="round" strokeLinejoin="round" d="M12 20.25c-4.97-3.12-8.25-6.16-8.25-10.03A4.72 4.72 0 0 1 8.5 5.5c1.5 0 2.93.7 3.84 1.92A4.8 4.8 0 0 1 16.18 5.5a4.72 4.72 0 0 1 4.82 4.72c0 3.87-3.28 6.91-8.25 10.03Z" />
    </svg>
  )
}

function DownloadIcon(props) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" aria-hidden="true" {...props}>
      <path strokeLinecap="round" strokeLinejoin="round" d="M12 3.75v10.5" />
      <path strokeLinecap="round" strokeLinejoin="round" d="m8.25 10.5 3.75 3.75 3.75-3.75" />
      <path strokeLinecap="round" strokeLinejoin="round" d="M4.5 18.75h15" />
    </svg>
  )
}

function ViewIcon(props) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" aria-hidden="true" {...props}>
      <path strokeLinecap="round" strokeLinejoin="round" d="M2.25 12s3.75-6.75 9.75-6.75S21.75 12 21.75 12 18 18.75 12 18.75 2.25 12 2.25 12Z" />
      <path strokeLinecap="round" strokeLinejoin="round" d="M12 14.75A2.75 2.75 0 1 0 12 9.25a2.75 2.75 0 0 0 0 5.5Z" />
    </svg>
  )
}

function HideIcon(props) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" aria-hidden="true" {...props}>
      <path strokeLinecap="round" strokeLinejoin="round" d="M6 6l12 12" />
      <path strokeLinecap="round" strokeLinejoin="round" d="M18 6 6 18" />
    </svg>
  )
}

function TagIcon(props) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" aria-hidden="true" {...props}>
      <path strokeLinecap="round" strokeLinejoin="round" d="M20.25 13.5 10.5 3.75H4.5v6l9.75 9.75a2.12 2.12 0 0 0 3 0l3-3a2.12 2.12 0 0 0 0-3Z" />
      <path strokeLinecap="round" strokeLinejoin="round" d="M7.875 7.875h.008v.008h-.008z" />
    </svg>
  )
}

function CollectionIcon(props) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" aria-hidden="true" {...props}>
      <path strokeLinecap="round" strokeLinejoin="round" d="M4.5 6.75A2.25 2.25 0 0 1 6.75 4.5h10.5a2.25 2.25 0 0 1 2.25 2.25v10.5a2.25 2.25 0 0 1-2.25 2.25H6.75a2.25 2.25 0 0 1-2.25-2.25V6.75Z" />
      <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 12h7.5" />
      <path strokeLinecap="round" strokeLinejoin="round" d="M12 8.25v7.5" />
    </svg>
  )
}

function ActionLink({ href, label, children, onClick }) {
  return (
    <a
      href={href || '#'}
      aria-label={label}
      onClick={onClick}
      className="inline-flex h-10 w-10 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white/90 shadow-[0_14px_36px_rgba(2,6,23,0.38)] backdrop-blur-md transition duration-200 hover:bg-white/20 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80"
    >
      {children}
    </a>
  )
}

function ActionButton({ label, children, onClick }) {
  return (
    <button
      type="button"
      aria-label={label}
      onClick={onClick}
      className="inline-flex h-10 w-10 items-center justify-center rounded-lg border border-white/10 bg-white/10 text-white/90 shadow-[0_14px_36px_rgba(2,6,23,0.38)] backdrop-blur-md transition duration-200 hover:bg-white/20 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80"
    >
      {children}
    </button>
  )
}

function BadgePill({ className = '', iconClass = '', children }) {
  return (
    <span
      className={cx(
        'inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] backdrop-blur-sm ring-1 shadow-[0_8px_24px_rgba(2,6,23,0.28)]',
        className
      )}
    >
      {iconClass ? <i className={iconClass} aria-hidden="true" /> : null}
      {children}
    </span>
  )
}

function CollectionPickerModal({
  open,
  artworkTitle,
  collections,
  loading,
  error,
  notice,
  createUrl,
  attachingCollectionId,
  onAttach,
  onClose,
}) {
  useEffect(() => {
    if (!open) return undefined

    const handleKeyDown = (event) => {
      if (event.key === 'Escape') {
        onClose()
      }
    }

    document.addEventListener('keydown', handleKeyDown)
    return () => document.removeEventListener('keydown', handleKeyDown)
  }, [open, onClose])

  if (!open) {
    return null
  }

  return (
    <div className="fixed inset-0 z-[140] flex items-center justify-center p-4">
      <button
        type="button"
        aria-label="Close add to collection dialog"
        onClick={onClose}
        className="absolute inset-0 bg-slate-950/80 backdrop-blur-sm"
      />

      <div className="relative z-10 w-full max-w-2xl overflow-hidden rounded-[28px] border border-white/10 bg-[linear-gradient(180deg,rgba(8,15,27,0.98),rgba(6,11,20,0.98))] shadow-[0_40px_120px_rgba(2,6,23,0.55)]">
        <div className="border-b border-white/10 px-6 py-5">
          <div className="flex items-start justify-between gap-4">
            <div>
              <p className="text-[11px] font-semibold uppercase tracking-[0.2em] text-sky-200/80">Collections</p>
              <h3 className="mt-2 text-2xl font-semibold tracking-[-0.03em] text-white">Add to collection</h3>
              <p className="mt-2 text-sm text-slate-300">Choose a showcase for <span className="font-semibold text-white">{artworkTitle}</span>.</p>
            </div>
            <button
              type="button"
              onClick={onClose}
              className="inline-flex h-11 w-11 items-center justify-center rounded-2xl border border-white/10 bg-white/[0.04] text-slate-300 transition hover:bg-white/[0.08] hover:text-white"
            >
              <i className="fa-solid fa-xmark" />
            </button>
          </div>
        </div>

        <div className="space-y-4 px-6 py-6">
          {notice ? <div className="rounded-2xl border border-emerald-400/20 bg-emerald-400/10 px-4 py-3 text-sm text-emerald-100">{notice}</div> : null}
          {error ? <div className="rounded-2xl border border-rose-400/20 bg-rose-400/10 px-4 py-3 text-sm text-rose-100">{error}</div> : null}

          {loading ? (
            <div className="rounded-[24px] border border-white/10 bg-white/[0.04] px-5 py-10 text-center text-sm text-slate-300">Loading collections...</div>
          ) : collections.length ? (
            <div className="grid gap-3 md:grid-cols-2">
              {collections.map((collection) => (
                <div key={collection.id} className="rounded-[24px] border border-white/10 bg-white/[0.04] p-4">
                  <div className="flex items-start justify-between gap-3">
                    <div className="min-w-0">
                      <div className="truncate text-sm font-semibold text-white">{collection.title}</div>
                      <div className="mt-1 text-xs text-slate-400">{collection.artworks_count} artworks • {collection.visibility}</div>
                    </div>
                    {collection.already_attached ? <span className="inline-flex items-center rounded-full border border-emerald-400/20 bg-emerald-400/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-emerald-100">Added</span> : null}
                  </div>
                  <div className="mt-4 flex flex-wrap gap-2">
                    <button
                      type="button"
                      onClick={() => onAttach(collection)}
                      disabled={collection.already_attached || attachingCollectionId === collection.id}
                      className={`inline-flex items-center gap-2 rounded-xl border px-3 py-2 text-xs font-semibold uppercase tracking-[0.14em] ${collection.already_attached || attachingCollectionId === collection.id ? 'border-white/8 bg-white/[0.03] text-slate-500' : 'border-sky-300/20 bg-sky-400/10 text-sky-100 transition hover:bg-sky-400/15'}`}
                    >
                      <CollectionIcon className="h-3.5 w-3.5" />
                      {attachingCollectionId === collection.id ? 'Adding...' : collection.already_attached ? 'Already added' : 'Add'}
                    </button>
                    <a href={collection.manage_url} className="inline-flex items-center gap-2 rounded-xl border border-white/12 bg-white/[0.05] px-3 py-2 text-xs font-semibold uppercase tracking-[0.14em] text-white transition hover:bg-white/[0.08]">Manage</a>
                  </div>
                </div>
              ))}
            </div>
          ) : (
            <div className="rounded-[24px] border border-white/10 bg-white/[0.04] px-5 py-10 text-center">
              <div className="mx-auto flex h-16 w-16 items-center justify-center rounded-[20px] border border-white/10 bg-white/[0.05] text-slate-400">
                <CollectionIcon className="h-7 w-7" />
              </div>
              <h4 className="mt-4 text-lg font-semibold text-white">Create your first collection</h4>
              <p className="mx-auto mt-2 max-w-md text-sm leading-relaxed text-slate-300">Start a curated showcase, then add this artwork into the sequence.</p>
              <a href={createUrl} className="mt-5 inline-flex items-center gap-2 rounded-2xl border border-sky-300/25 bg-sky-400/10 px-4 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15">
                <i className="fa-solid fa-plus" />
                Create Collection
              </a>
            </div>
          )}
        </div>
      </div>
    </div>
  )
}

export default function ArtworkCard({
  artwork,
  variant = 'default',
  compact = false,
  showStats = true,
  showAuthor = true,
  className = '',
  articleClassName = '',
  frameClassName = '',
  mediaClassName = '',
  mediaStyle,
  articleStyle,
  imageClassName = '',
  imageSizes,
  imageSrcSet,
  imageWidth,
  imageHeight,
  loading = 'lazy',
  decoding = 'async',
  fetchPriority,
  onLike,
  onDismissed,
  showActions = true,
  metricBadge = null,
}) {
  let inertiaProps = {}

  try {
    inertiaProps = usePage()?.props || {}
  } catch {
    inertiaProps = {}
  }

  const item = artwork || {}
  const rawAuthor = item.author || item.creator
  const publisher = item.publisher && typeof item.publisher === 'object' ? item.publisher : null
  const isGroupPublisher = (publisher?.type === 'group') || item.published_as_type === 'group'
  const title = decodeHtml(item.title || item.name || 'Untitled artwork')
  const author = decodeHtml(
    (isGroupPublisher ? publisher?.name : null)
      || (typeof rawAuthor === 'string' ? rawAuthor : rawAuthor?.name)
      || item.author_name
      || item.uname
      || 'Skinbase Artist'
  )
  const username = isGroupPublisher ? null : (rawAuthor?.username || item.author_username || item.username || null)
  const authorLevel = isGroupPublisher ? 0 : Number(rawAuthor?.level ?? item.author_level ?? item.creator?.level ?? 0)
  const authorRank = isGroupPublisher ? '' : (rawAuthor?.rank || item.author_rank || item.creator?.rank || '')
  const image = item.image || item.thumb || item.thumb_url || item.thumbnail_url || IMAGE_FALLBACK
  const responsiveImageSrcSet = imageSrcSet || item.thumb_srcset || item.thumbnail_srcset || undefined
  const responsiveImageSizes = imageSizes || (variant === 'embed'
    ? '80px'
    : compact
      ? '(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 280px'
      : '(max-width: 640px) 50vw, (max-width: 1024px) 33vw, (max-width: 1536px) 25vw, 320px')
  const avatar = (isGroupPublisher ? publisher?.avatar_url : null)
    || rawAuthor?.avatar_url
    || rawAuthor?.avatar
    || item.avatar
    || item.author_avatar
    || item.avatar_url
    || AVATAR_FALLBACK
  const likes = item.likes ?? item.favourites ?? 0
  const views = item.views ?? item.views_count ?? item.view_count ?? 0
  const downloads = item.downloads ?? item.downloads_count ?? item.download_count ?? 0
  const contentType = normalizeContentTypeLabel(
    item.content_type
      || item.content_type_name
      || item.contentType
      || item.contentTypeName
      || item.content_type_slug
      || ''
  )
  const category = decodeHtml(
    (typeof item.category === 'string' ? item.category : item.category?.name)
      || item.category_name
      || ''
  )
  const width = Number(item.width ?? 0)
  const height = Number(item.height ?? 0)
  const resolution = decodeHtml(item.resolution || ((width > 0 && height > 0) ? `${width}x${height}` : ''))
  const href = item.canonical_url || item.urls?.canonical || item.urls?.direct || item.url || item.href || (item.id ? `/art/${item.id}/${slugify(title)}` : '#')
  const downloadHref = item.download_url || item.downloadHref || (item.id ? `/download/artwork/${item.id}` : href)
  const cardLabel = `${title} by ${author}`
  const aspectClass = compact ? 'aspect-square' : 'aspect-[4/5]'
  const titleClass = compact ? 'text-[0.96rem]' : 'text-[1rem] sm:text-[1.08rem]'
  const metadataLine = [contentType, category, resolution].filter(Boolean).join(' • ')
  const authorHref = publisher?.profile_url || rawAuthor?.profile_url || item.profile_url || item.author_url || (username ? `/@${username}` : null)
  const resolvedMetricBadge = metricBadge || item.metric_badge || null
  const relativePublishedAt = useMemo(
    () => formatRelativeTime(item.published_at || item.publishedAt || null),
    [item.published_at, item.publishedAt]
  )
  const maturity = item.maturity && typeof item.maturity === 'object' ? item.maturity : {}
  const shouldBlurMature = Boolean(maturity.should_blur)
  const isMatureArtwork = Boolean(maturity.is_mature_effective)
  const initialLiked = Boolean(item.viewer?.is_liked)
  const [liked, setLiked] = useState(initialLiked)
  const [likeCount, setLikeCount] = useState(Number(likes ?? 0) || 0)
  const [likeBusy, setLikeBusy] = useState(false)
  const [downloadBusy, setDownloadBusy] = useState(false)
  const [hideBusy, setHideBusy] = useState(false)
  const [dislikeBusy, setDislikeBusy] = useState(false)
  const [dismissed, setDismissed] = useState(false)
  const [collectionPickerOpen, setCollectionPickerOpen] = useState(false)
  const [collectionOptionsLoading, setCollectionOptionsLoading] = useState(false)
  const [collectionOptionsLoaded, setCollectionOptionsLoaded] = useState(false)
  const [collectionOptions, setCollectionOptions] = useState([])
  const [collectionCreateUrl, setCollectionCreateUrl] = useState('/settings/collections/create')
  const [collectionPickerError, setCollectionPickerError] = useState('')
  const [collectionPickerNotice, setCollectionPickerNotice] = useState('')
  const [attachingCollectionId, setAttachingCollectionId] = useState(null)
  const openTrackedRef = useRef(false)
  const primaryTag = useMemo(() => {
    if (item?.primary_tag && typeof item.primary_tag === 'object') {
      return item.primary_tag
    }

    if (Array.isArray(item?.tags) && item.tags.length > 0) {
      return item.tags[0]
    }

    return null
  }, [item.primary_tag, item.tags])
  const hideArtworkEndpoint = item.hide_artwork_endpoint || item?.negative_feedback?.hide_artwork_endpoint || null
  const dislikeTagEndpoint = item.dislike_tag_endpoint || item?.negative_feedback?.dislike_tag_endpoint || null
  const canHideRecommendation = Boolean(item?.id && hideArtworkEndpoint && item?.recommendation_algo_version)
  const canDislikePrimaryTag = Boolean(dislikeTagEndpoint && item?.recommendation_algo_version && (primaryTag?.id || primaryTag?.slug))
  const authUserId = Number(inertiaProps?.auth?.user?.id ?? 0)
  const itemOwnerId = Number(item.author_id ?? rawAuthor?.id ?? item.user_id ?? item.creator?.id ?? 0)
  const canAddToCollection = Boolean(authUserId > 0 && Number(item.id ?? 0) > 0 && itemOwnerId > 0 && itemOwnerId === authUserId)
  const collectionOptionsEndpoint = canAddToCollection
    ? (item.collection_options_endpoint || `/settings/collections/artworks/${item.id}/options`)
    : null

  useEffect(() => {
    setLiked(Boolean(item.viewer?.is_liked))
    setLikeCount(Number(item.likes ?? item.favourites ?? 0) || 0)
    setDismissed(false)
    setCollectionPickerOpen(false)
    setCollectionOptionsLoading(false)
    setCollectionOptionsLoaded(false)
    setCollectionOptions([])
    setCollectionPickerError('')
    setCollectionPickerNotice('')
    setAttachingCollectionId(null)
  }, [item.id, item.likes, item.favourites, item.viewer?.is_liked])

  const articleData = useMemo(() => ({
    'data-art-id': item.id ?? undefined,
    'data-art-url': href !== '#' ? href : undefined,
    'data-art-title': title,
    'data-art-img': image,
  }), [href, image, item.id, title])

  const handleOpen = () => {
    if (openTrackedRef.current) return
    openTrackedRef.current = true

    trackRecommendationFeedback(item, 'click', {
      interaction_origin: 'artwork-card-open',
      target_url: href,
    })
  }

  const handleLike = async () => {
    if (!item.id || likeBusy) {
      onLike?.(item)
      return
    }

    const nextState = !liked
    setLikeBusy(true)
    setLiked(nextState)
    setLikeCount((current) => Math.max(0, current + (nextState ? 1 : -1)))

    try {
      const response = await fetch(`/api/artworks/${item.id}/like`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-CSRF-TOKEN': getCsrfToken(),
        },
        credentials: 'same-origin',
        body: JSON.stringify({ state: nextState }),
      })

      if (!response.ok) {
        throw new Error('like_request_failed')
      }

      if (nextState) {
        trackRecommendationFeedback(item, 'favorite', {
          interaction_origin: 'artwork-card-like',
        })
      }

      onLike?.(item)
    } catch {
      setLiked(!nextState)
      setLikeCount((current) => Math.max(0, current + (nextState ? -1 : 1)))
    } finally {
      setLikeBusy(false)
    }
  }

  const handleDownload = async (event) => {
    event.preventDefault()
    if (!item.id || downloadBusy) return

    setDownloadBusy(true)
    try {
      trackRecommendationFeedback(item, 'download', {
        interaction_origin: 'artwork-card-download',
        target_url: downloadHref,
      })

      const link = document.createElement('a')
      link.href = downloadHref
      link.rel = 'noopener noreferrer'
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
    } catch {
      window.open(downloadHref, '_blank', 'noopener,noreferrer')
    } finally {
      setDownloadBusy(false)
    }
  }

  const dismissArtwork = (kind) => {
    setDismissed(true)
    onDismissed?.(item, kind)
  }

  const handleHideArtwork = async (event) => {
    event.preventDefault()
    event.stopPropagation()
    if (!canHideRecommendation || hideBusy) return

    setHideBusy(true)
    try {
      await sendFeedbackSignal(hideArtworkEndpoint, {
        artwork_id: Number(item.id),
        algo_version: item.recommendation_algo_version || item.algo_version || undefined,
        source: item.recommendation_source || 'recommendation-card',
        meta: {
          gallery_type: item.recommendation_surface || item.gallery_type || 'recommendation-surface',
          reason: item.recommendation_reason || null,
          primary_tag_slug: primaryTag?.slug || null,
          interaction_origin: 'artwork-card-hide',
        },
      })

      dismissArtwork('hide-artwork')
    } catch {
      // Keep the card visible if the feedback request fails.
    } finally {
      setHideBusy(false)
    }
  }

  const handleDislikePrimaryTag = async (event) => {
    event.preventDefault()
    event.stopPropagation()
    if (!canDislikePrimaryTag || dislikeBusy) return

    setDislikeBusy(true)
    try {
      await sendFeedbackSignal(dislikeTagEndpoint, {
        tag_id: primaryTag?.id ? Number(primaryTag.id) : undefined,
        tag_slug: primaryTag?.slug || undefined,
        algo_version: item.recommendation_algo_version || item.algo_version || undefined,
        source: item.recommendation_source || 'recommendation-card',
        meta: {
          artwork_id: Number(item.id),
          gallery_type: item.recommendation_surface || item.gallery_type || 'recommendation-surface',
          reason: item.recommendation_reason || null,
          interaction_origin: 'artwork-card-dislike-tag',
        },
      })

      dismissArtwork('dislike-tag')
    } catch {
      // Keep the card visible if the feedback request fails.
    } finally {
      setDislikeBusy(false)
    }
  }

  const handleOpenCollectionPicker = async (event) => {
    event.preventDefault()
    event.stopPropagation()
    if (!collectionOptionsEndpoint || collectionOptionsLoading) return

    setCollectionPickerOpen(true)
    setCollectionPickerError('')
    setCollectionPickerNotice('')

    if (collectionOptionsLoaded) {
      return
    }

    setCollectionOptionsLoading(true)
    try {
      const payload = await requestJson(collectionOptionsEndpoint)
      setCollectionOptions(Array.isArray(payload?.data) ? payload.data : [])
      setCollectionCreateUrl(payload?.meta?.create_url || '/settings/collections/create')
      setCollectionOptionsLoaded(true)
    } catch (error) {
      setCollectionPickerError(error.message || 'Unable to load collections.')
    } finally {
      setCollectionOptionsLoading(false)
    }
  }

  const handleAttachToCollection = async (collection) => {
    if (!collection?.attach_url || attachingCollectionId === collection.id) return

    setAttachingCollectionId(collection.id)
    setCollectionPickerError('')
    setCollectionPickerNotice('')

    try {
      await requestJson(collection.attach_url, {
        method: 'POST',
        body: { artwork_ids: [Number(item.id)] },
      })

      setCollectionOptions((current) => current.map((entry) => (
        entry.id === collection.id
          ? { ...entry, already_attached: true, artworks_count: Number(entry.artworks_count || 0) + 1 }
          : entry
      )))
      setCollectionPickerNotice(`Added to ${collection.title}.`)
    } catch (error) {
      const firstError = error?.payload?.errors
        ? Object.values(error.payload.errors).flat().find(Boolean)
        : null
      setCollectionPickerError(firstError || error.message || 'Unable to add artwork to collection.')
    } finally {
      setAttachingCollectionId(null)
    }
  }

  if (dismissed) {
    return null
  }

  if (maturity.should_hide) {
    return null
  }

  if (variant === 'embed') {
    return (
      <article
        className={cx('group overflow-hidden rounded-xl border border-white/[0.08] bg-black/30 transition-colors hover:border-sky-500/30', articleClassName, className)}
        style={articleStyle}
        {...articleData}
      >
        <a
          href={href}
          className="flex gap-3 p-3 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80"
          aria-label={`Open artwork: ${cardLabel}`}
          onClick={handleOpen}
        >
          <div className="h-16 w-20 shrink-0 overflow-hidden rounded-lg bg-white/5">
            <img
              src={image}
              srcSet={responsiveImageSrcSet}
              sizes={responsiveImageSizes}
              alt={title}
              loading={loading}
              decoding={decoding}
              className={cx('h-full w-full object-cover transition-transform duration-300 group-hover:scale-105', shouldBlurMature ? 'scale-[1.02] blur-xl' : '')}
              onError={(event) => {
                swapImageToFallbackOnce(event, IMAGE_FALLBACK)
              }}
            />
            {isMatureArtwork ? <div className="absolute inset-x-2 bottom-2 rounded-lg border border-amber-300/20 bg-black/65 px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-amber-100">Mature content</div> : null}
          </div>

          <div className="min-w-0 flex-1">
            <p className="truncate text-sm font-medium text-white/90">{title}</p>
            {showAuthor && (
              <div className="mt-0.5 flex items-center gap-2 text-xs text-slate-400">
                {authorHref ? (
                  <span>
                    by {author} {username ? <span className="text-slate-500">@{username}</span> : null}
                  </span>
                ) : (
                  <span>by {author}</span>
                )}
                {authorLevel > 0 ? <LevelBadge level={authorLevel} rank={authorRank} compact /> : null}
              </div>
            )}
            <p className="mt-1 truncate text-[10px] uppercase tracking-wider text-slate-600">
              {contentType || 'Artwork'}
            </p>
          </div>
        </a>
      </article>
    )
  }

  return (
    <>
      <article
        className={cx('group relative', articleClassName, className)}
        style={articleStyle}
        {...articleData}
      >
        <div className={cx('relative overflow-hidden rounded-[1.6rem] border border-white/8 bg-slate-950/80 shadow-[0_18px_60px_rgba(2,6,23,0.45)] transition duration-300 ease-out group-hover:-translate-y-1 group-hover:scale-[1.02] group-hover:border-white/14 group-hover:shadow-[0_24px_80px_rgba(8,47,73,0.5)] group-focus-within:-translate-y-1 group-focus-within:scale-[1.02] group-focus-within:border-sky-200/40', frameClassName)}>
        <a
          href={href}
          aria-label={`Open artwork: ${cardLabel}`}
          onClick={handleOpen}
          className="absolute inset-0 z-10 rounded-[inherit] cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80"
        >
          <span className="sr-only">{cardLabel}</span>
        </a>

        <div className={cx('relative overflow-hidden bg-slate-900', aspectClass, mediaClassName)} style={mediaStyle}>
          <div className="absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(148,163,184,0.28),_transparent_52%),linear-gradient(180deg,rgba(15,23,42,0.08),rgba(15,23,42,0.42))]" />

          <img
            src={image}
            srcSet={responsiveImageSrcSet}
            sizes={responsiveImageSizes}
            alt={title}
            width={imageWidth || undefined}
            height={imageHeight || undefined}
            loading={loading}
            decoding={decoding}
            fetchPriority={fetchPriority || undefined}
            className={cx('h-full w-full object-cover transition duration-500 group-hover:scale-110 group-focus-within:scale-110', shouldBlurMature ? 'scale-[1.02] blur-xl' : '', imageClassName)}
            onError={(event) => {
              swapImageToFallbackOnce(event, IMAGE_FALLBACK, { clearResponsive: true })
            }}
          />

          <div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-black/85 via-black/38 to-transparent opacity-90 transition duration-300 md:opacity-45 md:group-hover:opacity-100 md:group-focus-within:opacity-100" />

          {(resolvedMetricBadge?.label || relativePublishedAt) ? (
            <div className="pointer-events-none absolute inset-x-0 top-0 z-20 flex items-start justify-between gap-3 p-3">
              <div>
                {resolvedMetricBadge?.label ? (
                  <BadgePill className={resolvedMetricBadge.className || 'bg-emerald-500/14 text-emerald-200 ring-emerald-400/30'} iconClass={resolvedMetricBadge.iconClass}>
                    {resolvedMetricBadge.label}
                  </BadgePill>
                ) : null}
                {isMatureArtwork ? (
                  <BadgePill className="mt-2 bg-amber-500/16 text-amber-100 ring-amber-300/30" iconClass="fa-solid fa-triangle-exclamation text-[10px]">
                    Mature content
                  </BadgePill>
                ) : null}
              </div>

              {relativePublishedAt ? (
                <BadgePill className="bg-black/45 text-white/75 ring-white/12" iconClass="fa-regular fa-clock text-[10px]">
                  {relativePublishedAt}
                </BadgePill>
              ) : null}
            </div>
          ) : null}

          {showActions && (
            <div className={cx(
              'absolute right-3 z-20 flex max-w-[14rem] flex-wrap justify-end translate-y-2 gap-2 opacity-100 transition duration-200 md:opacity-0 md:group-hover:translate-y-0 md:group-hover:opacity-100 md:group-focus-within:translate-y-0 md:group-focus-within:opacity-100',
              relativePublishedAt ? 'top-12' : 'top-3'
            )}>
              <ActionButton label={liked ? 'Unlike artwork' : 'Like artwork'} onClick={handleLike}>
                <HeartIcon className={cx('h-4 w-4 transition-transform duration-200', liked ? 'fill-current text-rose-300' : '', likeBusy ? 'scale-90' : '')} />
              </ActionButton>

              <ActionLink href={downloadHref} label={downloadBusy ? 'Downloading artwork' : 'Download artwork'} onClick={handleDownload}>
                <DownloadIcon className={cx('h-4 w-4', downloadBusy ? 'animate-pulse text-emerald-300' : '')} />
              </ActionLink>

              <ActionLink href={href} label="View artwork">
                <ViewIcon className="h-4 w-4" />
              </ActionLink>

              {canAddToCollection ? (
                <ActionButton label="Add artwork to collection" onClick={handleOpenCollectionPicker}>
                  <CollectionIcon className="h-4 w-4" />
                </ActionButton>
              ) : null}

              {canHideRecommendation ? (
                <ActionButton label={hideBusy ? 'Hiding artwork' : 'Hide artwork'} onClick={handleHideArtwork}>
                  <HideIcon className={cx('h-4 w-4', hideBusy ? 'animate-pulse text-amber-200' : 'text-white/90')} />
                </ActionButton>
              ) : null}
            </div>
          )}

          <div className="pointer-events-none absolute inset-x-0 bottom-0 z-20 bg-gradient-to-t from-black/80 via-black/40 to-transparent p-3 backdrop-blur-[2px] opacity-100 transition-opacity duration-200 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100">
              {shouldBlurMature ? <div className="mb-2 inline-flex rounded-full border border-amber-300/20 bg-black/55 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.16em] text-amber-100">Blurred by your content settings</div> : null}
              <h3 className={cx('truncate font-semibold text-white', titleClass)}>
                {title}
              </h3>

              {showAuthor ? (
                <div className="mt-1 flex items-start justify-between gap-3 text-xs text-white/80">
                  <span className="flex min-w-0 items-start gap-3">
                    <img
                      src={avatar}
                      alt={`Avatar of ${author}`}
                      loading="lazy"
                      decoding="async"
                      className="h-9 w-9 shrink-0 rounded-full object-cover"
                      onError={(event) => {
                        swapImageToFallbackOnce(event, AVATAR_FALLBACK)
                      }}
                    />
                    <span className="min-w-0 flex-1">
                      <span className="flex items-center gap-2">
                        <span className="block min-w-0 truncate text-sm font-medium text-white/90">
                          {author}
                          {username ? <span className="text-[11px] text-white/60"> @{username}</span> : null}
                        </span>
                        {authorLevel > 0 ? <LevelBadge level={authorLevel} rank={authorRank} compact className="shrink-0" /> : null}
                      </span>
                      {showStats && metadataLine && (
                        <span className="mt-0.5 block truncate text-[11px] text-white/70">
                          {metadataLine}
                        </span>
                      )}
                    </span>
                  </span>
                </div>
              ) : showStats && metadataLine ? (
                <div className="mt-1 text-[11px] text-white/70">
                  {metadataLine}
                </div>
              ) : null}

              {canDislikePrimaryTag ? (
                <div className="pointer-events-auto mt-2">
                  <button
                    type="button"
                    onClick={handleDislikePrimaryTag}
                    className="inline-flex items-center gap-1.5 rounded-full border border-white/12 bg-black/35 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-white/80 transition hover:border-amber-200/40 hover:bg-black/55 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/80"
                  >
                    <TagIcon className={cx('h-3.5 w-3.5', dislikeBusy ? 'animate-pulse text-amber-200' : '')} />
                    {dislikeBusy ? 'Updating' : `Less of #${primaryTag?.slug || primaryTag?.name}`}
                  </button>
                </div>
              ) : null}
          </div>
        </div>
        </div>
      </article>

      <CollectionPickerModal
        open={collectionPickerOpen}
        artworkTitle={title}
        collections={collectionOptions}
        loading={collectionOptionsLoading}
        error={collectionPickerError}
        notice={collectionPickerNotice}
        createUrl={collectionCreateUrl}
        attachingCollectionId={attachingCollectionId}
        onAttach={handleAttachToCollection}
        onClose={() => setCollectionPickerOpen(false)}
      />
    </>
  )
}
