// frontend\src\components\ui\FinishedDownloadsDesktopCard.tsx

'use client'

import { memo, useCallback, type ReactNode } from 'react'
import Card from './Card'
import Checkbox from './Checkbox'
import CountryFlag, { resolveFinishedCountry } from './CountryFlag'
import FinishedVideoPreview from './FinishedVideoPreview'
import ModelGenderIcon from './ModelGenderIcon'
import RecordJobActions from './RecordJobActions'
import TagOverflowRow from './TagOverflowRow'
import { parseFinishedTags, resolveFinishedModelFlags } from './finishedModelFlags'
import { isHotName, stripHotPrefix } from './hotName'
import {
  type FinishedSelectionStore,
  useSelectionChecked,
} from './finishedSelectionStore'
import type {
  FinishedPostworkSummary,
  GalleryModelFlags,
  InlinePlayState,
  RecordJob,
} from '../../types'
import type { FinishedDownloadsTeaserState } from './teaserPlayback'
import {
  shouldAnimateTeaser,
  shouldEnableTeaserAudio,
} from './teaserPlayback'
import { parseJobMeta } from './previewSprite'
import PreviewRatingOverlay from './PreviewRatingOverlay'
import PreviewMetaOverlay from './PreviewMetaOverlay'

export type FinishedDownloadsDesktopCardProps = {
  job: RecordJob
  selectionStore: FinishedSelectionStore
  isDeleting: boolean
  isKeeping: boolean
  isRemoving: boolean
  postworkBadge?: FinishedPostworkSummary

  blurPreviews?: boolean
  durationSeconds?: number
  teaserState: FinishedDownloadsTeaserState
  inlinePlay: InlinePlayState
  assetNonce?: number
  forcePreviewMuted?: boolean

  keyFor: (j: RecordJob) => string
  baseName: (p: string) => string
  modelNameFromOutput: (output?: string) => string
  runtimeOf: (job: RecordJob) => string
  sizeBytesOf: (job: RecordJob) => number | null
  formatBytes: (bytes?: number | null) => string
  lower: (s: string) => string

  jobForDetails: (job: RecordJob) => RecordJob
  handleDuration: (job: RecordJob, seconds: number) => void
  handleResolution: (job: RecordJob, w: number, h: number) => void
  resolutionLabelOf: (job: RecordJob) => string

  onToggleSelected: (job: RecordJob) => void
  onHoverPreviewKeyChange?: (key: string | null) => void
  onOpenPlayer: (job: RecordJob) => void
  openPlayer: (job: RecordJob) => void
  startInline: (key: string) => void
  registerTeaserHost: (key: string) => (el: HTMLDivElement | null) => void

  modelsByKey: Record<string, GalleryModelFlags>
  activeTagSet: Set<string>
  onToggleTagFilter: (tag: string) => void
  onToggleHot?: (job: RecordJob) => void | Promise<void>
  onToggleFavorite?: (job: RecordJob) => void | Promise<void>
  onToggleLike?: (job: RecordJob) => void | Promise<void>
  onToggleWatch?: (job: RecordJob) => void | Promise<void>
  onSplit?: (job: RecordJob) => void | Promise<void>
  onAddToDownloads?: (job: RecordJob) => void | Promise<boolean>
  deleteVideo: (job: RecordJob) => Promise<boolean>
  keepVideo: (job: RecordJob) => Promise<boolean>

  renderPostworkBadge?: (badge?: FinishedPostworkSummary) => ReactNode
  renderIntegrityBadge?: (job: RecordJob) => ReactNode
  renderRatingOverlay?: (job: RecordJob) => ReactNode
}

function samePostworkSummaryLite(
  a?: FinishedPostworkSummary,
  b?: FinishedPostworkSummary
) {
  if (a === b) return true
  if (!a || !b) return !a && !b

  return (
    a.file === b.file &&
    a.state === b.state &&
    a.label === b.label &&
    a.ts === b.ts
  )
}

function isPreviewActiveForKey(
  state: FinishedDownloadsTeaserState,
  key: string
) {
  return state.activeKey === key || state.hoverKey === key
}

function inlineStateForKey(
  inlinePlay: InlinePlayState,
  key: string
) {
  const active = inlinePlay?.key === key
  return {
    active,
    nonce: active ? inlinePlay?.nonce ?? 0 : 0,
  }
}

export function SelectionCheckboxOverlay({
  selectionStore,
  rowKey,
  label,
  onToggle,
}: {
  selectionStore: FinishedSelectionStore
  rowKey: string
  label: string
  onToggle: () => void
}) {
  const checked = useSelectionChecked(selectionStore, rowKey)

  return (
    <div
      className={[
        'absolute left-3 top-3 z-20 transition-opacity duration-150',
        checked ? 'opacity-100' : 'opacity-0 group-hover:opacity-100 group-focus-within:opacity-100',
      ].join(' ')}
      onClick={(e) => e.stopPropagation()}
      onPointerDown={(e) => e.stopPropagation()}
    >
      <Checkbox
        checked={checked}
        ariaLabel={label}
        onChange={onToggle}
      />
    </div>
  )
}

const FinishedDownloadsDesktopCard = memo(
  function FinishedDownloadsDesktopCard({
    job: j,
    selectionStore,
    isDeleting,
    isKeeping,
    isRemoving,
    postworkBadge,
    blurPreviews,
    durationSeconds,
    teaserState,
    inlinePlay,
    assetNonce,
    forcePreviewMuted,
    keyFor,
    baseName,
    modelNameFromOutput,
    runtimeOf,
    sizeBytesOf,
    formatBytes,
    lower,
    jobForDetails,
    handleDuration,
    handleResolution,
    resolutionLabelOf,
    onToggleSelected,
    onHoverPreviewKeyChange,
    onOpenPlayer,
    openPlayer,
    startInline,
    registerTeaserHost,
    modelsByKey,
    activeTagSet,
    onToggleTagFilter,
    onToggleHot,
    onToggleFavorite,
    onToggleLike,
    onToggleWatch,
    onSplit,
    onAddToDownloads,
    deleteVideo,
    keepVideo,
    renderPostworkBadge,
    renderIntegrityBadge,
    renderRatingOverlay,
  }: FinishedDownloadsDesktopCardProps) {
    const k = keyFor(j)
    const busy = isDeleting || isKeeping || isRemoving
    const inlineActive = inlinePlay?.key === k
    const inlineNonce = inlineActive ? inlinePlay?.nonce ?? 0 : 0

    const allowSound = shouldEnableTeaserAudio({
      state: teaserState,
      itemKey: k,
      inlineActive,
      disabled: false,
    })

    const handleSelectOrOpen = useCallback(() => {
      if (selectionStore.hasAnySelection()) {
        onToggleSelected(j)
        return true
      }
      return false
    }, [selectionStore, onToggleSelected, j])

    const previewMuted = forcePreviewMuted ? true : !allowSound

    const model = modelNameFromOutput(j.output)
    const fileRaw = baseName(j.output || '')
    const isHot = isHotName(fileRaw)

    const { flags } = resolveFinishedModelFlags(
      j,
      modelsByKey,
      modelNameFromOutput,
      lower
    )

    const tags = parseFinishedTags(flags?.tags)
    const modelCountry = resolveFinishedCountry(flags, j)
    const dur = runtimeOf(j)
    const size = formatBytes(sizeBytesOf(j))

    const meta = parseJobMeta((j as any)?.meta)

    const resObj = (() => {
      const w = meta?.media?.video?.width ?? meta?.file?.video?.width
      const h = meta?.media?.video?.height ?? meta?.file?.video?.height

      if (
        typeof w === 'number' && Number.isFinite(w) && w > 0 &&
        typeof h === 'number' && Number.isFinite(h) && h > 0
      ) {
        return { w, h }
      }

      return null
    })()

    const resLabelRaw = resolutionLabelOf(j)
    const resLabel = resLabelRaw && resLabelRaw !== '—' ? resLabelRaw : ''

    const inlineDomId = `inline-prev-${encodeURIComponent(k)}`

    return (
      <div
        role="button"
        tabIndex={0}
        data-finished-hover-key={k}
        className={[
          'group relative rounded-lg overflow-visible outline-1 outline-black/5 dark:-outline-offset-1 dark:outline-white/10',
          'bg-white dark:bg-gray-900/40',
          'transition-all duration-200',
          'hover:-translate-y-0.5 hover:shadow-md dark:hover:shadow-none',
          'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 dark:focus-visible:outline-indigo-500',
          busy && 'pointer-events-none opacity-70',
          isDeleting && 'ring-1 ring-red-300 bg-red-50/60 dark:bg-red-500/10 dark:ring-red-500/30',
          isKeeping && 'ring-1 ring-emerald-300 bg-emerald-50/60 dark:bg-emerald-500/10 dark:ring-emerald-500/30',
          isRemoving && 'opacity-0 translate-y-2 scale-[0.98]',
        ].filter(Boolean).join(' ')}
        onClick={(e) => {
          e.preventDefault()
          e.stopPropagation()

          if (handleSelectOrOpen()) return
          openPlayer(j)
        }}
        onMouseEnter={() => onHoverPreviewKeyChange?.(k)}
        onMouseLeave={() => onHoverPreviewKeyChange?.(null)}
        onKeyDown={(e) => {
          if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault()

            if (handleSelectOrOpen()) return
            onOpenPlayer(j)
          }
        }}
      >
        <SelectionCheckboxOverlay
          selectionStore={selectionStore}
          rowKey={k}
          label={`Auswählen: ${baseName(j.output || '') || 'Download'}`}
          onToggle={() => onToggleSelected(j)}
        />

        <Card noBodyPadding className="overflow-hidden bg-transparent">
          <div
            id={inlineDomId}
            ref={registerTeaserHost(k)}
            className="group/thumb relative aspect-video rounded-t-lg bg-black/5 dark:bg-white/5"
            onClick={(e) => {
              e.preventDefault()
              e.stopPropagation()

              if (handleSelectOrOpen()) return
              startInline(k)
            }}
          >
            <div className="absolute inset-0 overflow-hidden rounded-t-lg">
              <PreviewRatingOverlay hidden={inlineActive}>
                {renderRatingOverlay ? renderRatingOverlay(j) : null}
              </PreviewRatingOverlay>

              <PreviewMetaOverlay
                hidden={inlineActive}
                dur={dur}
                resObj={resObj}
                resLabel={resLabel}
                size={size}
              />

              <FinishedVideoPreview
                job={j}
                getFileName={(p) => stripHotPrefix(baseName(p))}
                className="h-full w-full"
                variant="fill"
                durationSeconds={durationSeconds ?? (j as any)?.meta?.file?.durationSeconds}
                onDuration={handleDuration}
                onResolution={handleResolution}
                showPopover={false}
                blur={inlineActive ? false : Boolean(blurPreviews)}
                animated={shouldAnimateTeaser({
                  state: teaserState,
                  itemKey: k,
                  disabled: false,
                })}
                animatedMode="teaser"
                animatedTrigger="always"
                inlineVideo={inlineActive ? 'always' : false}
                inlineNonce={inlineNonce}
                inlineControls={inlineActive}
                inlineLoop={false}
                muted={previewMuted}
                popoverMuted={previewMuted}
                assetNonce={assetNonce ?? 0}
              />
            </div>
          </div>

          <div
            className="relative min-h-[112px] overflow-visible border-t border-black/5 bg-white px-4 py-3 dark:border-white/10 dark:bg-gray-900"
            onClick={(e) => {
              e.preventDefault()
              e.stopPropagation()

              if (handleSelectOrOpen()) return
              openPlayer(j)
            }}
          >
            <div className="flex min-h-6 items-center justify-between gap-2">
              <div className="min-w-0 flex-1">
                <div className="flex min-h-6 min-w-0 items-center justify-between gap-2">
                  <div className="min-w-0 flex items-center gap-1.5">
                    <div className="min-w-0 truncate text-sm font-semibold text-gray-900 dark:text-white">
                      {model}
                    </div>

                    <ModelGenderIcon gender={flags?.gender} className="shrink-0" />
                    <CountryFlag country={modelCountry} size="sm" className="shrink-0" />
                  </div>

                  {(renderIntegrityBadge || (renderPostworkBadge && postworkBadge)) ? (
                    <div className="relative z-20 flex h-6 shrink-0 items-center gap-2">
                      {renderIntegrityBadge ? renderIntegrityBadge(j) : null}
                      {renderPostworkBadge && postworkBadge ? renderPostworkBadge(postworkBadge) : null}
                    </div>
                  ) : null}
                </div>

                <div className="mt-0.5 flex min-w-0 items-start gap-2">
                  {isHot ? (
                    <span className="shrink-0 self-start rounded bg-amber-500/15 px-1.5 py-0.5 text-[11px] font-semibold leading-none text-amber-800 dark:text-amber-300">
                      HOT
                    </span>
                  ) : null}

                  <span className="min-w-0 truncate text-xs text-gray-500 dark:text-gray-400">
                    {stripHotPrefix(fileRaw) || '—'}
                  </span>
                </div>
              </div>
            </div>

            <div
              className="mt-2"
              onClick={(e) => e.stopPropagation()}
              onMouseDown={(e) => e.stopPropagation()}
            >
              <div className="w-full rounded-md bg-gray-50/70 p-1 ring-1 ring-black/5 dark:bg-white/5 dark:ring-white/10">
                <RecordJobActions
                  job={jobForDetails(j)}
                  variant="table"
                  busy={busy}
                  collapseToMenu
                  compact={false}
                  isHot={isHot}
                  isFavorite={Boolean(flags?.favorite)}
                  isLiked={flags?.liked === true}
                  isWatching={Boolean(flags?.watching)}
                  onToggleWatch={onToggleWatch}
                  onToggleFavorite={onToggleFavorite}
                  onToggleLike={onToggleLike}
                  onToggleHot={onToggleHot}
                  onKeep={keepVideo}
                  onDelete={deleteVideo}
                  onSplit={onSplit}
                  onAddToDownloads={onAddToDownloads}
                  order={['watch', 'favorite', 'like', 'hot', 'keep', 'delete', 'split', 'details', 'add']}
                  className="w-full gap-1.5"
                />
              </div>
            </div>

            <div
              className="mt-2"
              onClick={(e) => e.stopPropagation()}
              onMouseDown={(e) => e.stopPropagation()}
            >
              <TagOverflowRow
                rowKey={k}
                tags={tags}
                activeTagSet={activeTagSet}
                lower={lower}
                onToggleTagFilter={onToggleTagFilter}
              />
            </div>
          </div>
        </Card>
      </div>
    )
  },
  (prev, next) => {
    const prevKey = prev.keyFor(prev.job)
    const nextKey = next.keyFor(next.job)

    const prevPreviewActive = isPreviewActiveForKey(prev.teaserState, prevKey)
    const nextPreviewActive = isPreviewActiveForKey(next.teaserState, nextKey)

    const prevInline = inlineStateForKey(prev.inlinePlay, prevKey)
    const nextInline = inlineStateForKey(next.inlinePlay, nextKey)

    return (
      prev.job === next.job &&
      prev.isDeleting === next.isDeleting &&
      prev.isKeeping === next.isKeeping &&
      prev.isRemoving === next.isRemoving &&
      samePostworkSummaryLite(prev.postworkBadge, next.postworkBadge) &&
      prev.durationSeconds === next.durationSeconds &&
      prev.assetNonce === next.assetNonce &&
      prev.forcePreviewMuted === next.forcePreviewMuted &&
      prev.blurPreviews === next.blurPreviews &&
      prev.teaserState.mode === next.teaserState.mode &&
      prev.teaserState.audioEnabled === next.teaserState.audioEnabled &&
      prevPreviewActive === nextPreviewActive &&
      prevInline.active === nextInline.active &&
      prevInline.nonce === nextInline.nonce &&
      prev.modelsByKey === next.modelsByKey &&
      prev.activeTagSet === next.activeTagSet &&
      prev.renderPostworkBadge === next.renderPostworkBadge
    )
  }
)

export default FinishedDownloadsDesktopCard