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

'use client'

import { useMemo, useState, useRef, useEffect } from 'react'
import Button from './Button'
import ModelPreview from './ModelPreview'
import ModelGenderIcon from './ModelGenderIcon'
import ProgressBar from './ProgressBar'
import RecordJobActions from './RecordJobActions'
import type { RecordJob } from '../../types'
import LazyMountWhenVisible from './LazyMountWhenVisible'
import CountryFlag from './CountryFlag'

export type PendingWatchedRoom = WaitingModelRow & {
  currentShow: string
}

type WaitingModelRow = {
  id: string
  modelKey: string
  url: string
  imageUrl?: string
  currentShow?: string
}

export type DownloadRow =
  | { kind: 'job'; job: RecordJob }
  | { kind: 'pending'; pending: PendingWatchedRoom }

type ModelFlags = {
  favorite?: boolean
  liked?: boolean | null
  watching?: boolean
  roomStatus?: string
  imageUrl?: string
  gender?: string | null
  country?: string | null
}

type Props = {
  r: DownloadRow
  nowMs: number
  blurPreviews?: boolean
  modelsByKey: Record<string, ModelFlags>
  roomStatusByModelKey: Record<string, string>
  stopRequestedIds: Record<string, true>
  growingByJobId: Record<string, boolean>
  postworkInfoOf: (job: RecordJob) => { pos?: number; total?: number } | undefined
  markStopRequested: (ids: string | string[]) => void
  onOpenPlayer: (job: RecordJob) => void
  onStopJob: (id: string) => void | Promise<void>
  onRemoveQueuedPostworkJob?: (id: string) => void | Promise<void>
  onToggleFavorite?: (job: RecordJob) => void | Promise<void>
  onToggleLike?: (job: RecordJob) => void | Promise<void>
  onToggleWatch?: (job: RecordJob) => void | Promise<void>
}

const pendingModelName = (p: PendingWatchedRoom) => {
  const anyP = p as any
  return (
    anyP.modelName ??
    anyP.model ??
    anyP.modelKey ??
    anyP.username ??
    anyP.name ??
    '—'
  )
}

const pendingUrl = (p: PendingWatchedRoom) => {
  const anyP = p as any
  return anyP.sourceUrl ?? anyP.url ?? anyP.roomUrl ?? ''
}

const pendingImageUrl = (p: PendingWatchedRoom) => {
  const anyP = p as any
  return String(anyP.imageUrl ?? anyP.image_url ?? '').trim()
}

const normalizeRoomStatus = (v: unknown): string => {
  const s = String(v ?? '').trim().toLowerCase()

  switch (s) {
    case 'public':
      return 'Public'
    case 'private':
      return 'Private'
    case 'hidden':
      return 'Hidden'
    case 'away':
      return 'Away'
    case 'offline':
      return 'Offline'
    default:
      return 'Unknown'
  }
}

const baseName = (p: string) =>
  (p || '').replaceAll('\\', '/').trim().split('/').pop() || ''

const modelNameFromOutput = (output?: string) => {
  const file = baseName(output || '')
  if (!file) return '—'
  const stem = file.replace(/\.[^.]+$/, '')

  const m = stem.match(/^(.*?)_\d{1,2}_\d{1,2}_\d{4}__\d{1,2}-\d{2}-\d{2}$/)
  if (m?.[1]) return m[1]

  const i = stem.lastIndexOf('_')
  return i > 0 ? stem.slice(0, i) : stem
}

const modelKeyFromJob = (job: RecordJob): string => {
  const j = job as any

  const rawUrl = String(j.sourceUrl ?? '').trim()
  if (rawUrl) {
    const m = rawUrl.match(/chaturbate\.com\/([^/?#]+)/i)
    if (m?.[1]) {
      try {
        return decodeURIComponent(m[1]).trim().toLowerCase()
      } catch {
        return String(m[1]).trim().toLowerCase()
      }
    }
  }

  return String(modelNameFromOutput(j.output || '')).trim().toLowerCase()
}

const previewInitialSrcOfJob = (
  job: RecordJob,
  modelsByKey?: Record<string, { imageUrl?: string }>
): string => {
  return onlineImageUrlOfJob(job, modelsByKey)
}

const onlineImageUrlOfJob = (
  job: RecordJob,
  modelsByKey?: Record<string, { imageUrl?: string }>
): string => {
  const anyJ = job as any

  const direct =
    String(
      anyJ?.modelImageUrl ??
      anyJ?.avatar ??
      anyJ?.previewImageUrl ??
      anyJ?.imageUrl ??
      anyJ?.model?.imageUrl ??
      ''
    ).trim()

  if (direct) return direct

  const modelKey = modelKeyFromJob(job)
  if (!modelKey) return ''

  return String(modelsByKey?.[modelKey]?.imageUrl ?? '').trim()
}

const roomStatusOfJob = (
  job: RecordJob,
  roomStatusByModelKey?: Record<string, string>,
  modelsByKey?: Record<string, { roomStatus?: string }>
): string => {
  const j = job as any

  const rawFromJob =
    j.currentShow ??
    j.roomStatus ??
    j.modelStatus ??
    j.show ??
    j.statusText ??
    j.model?.currentShow ??
    j.model?.roomStatus ??
    j.model?.status ??
    j.room?.currentShow ??
    j.room?.status

  if (rawFromJob != null && String(rawFromJob).trim() !== '') {
    return normalizeRoomStatus(rawFromJob)
  }

  const modelKey = modelKeyFromJob(job)

  if (modelKey && roomStatusByModelKey?.[modelKey]) {
    return normalizeRoomStatus(roomStatusByModelKey[modelKey])
  }

  if (modelKey && modelsByKey?.[modelKey]?.roomStatus) {
    return normalizeRoomStatus(modelsByKey[modelKey].roomStatus)
  }

  return 'Unknown'
}

function effectiveRoomStatusOfJob(
  job: RecordJob,
  roomStatusByModelKey?: Record<string, string>,
  modelsByKey?: Record<string, { roomStatus?: string }>,
  growingByJobId?: Record<string, boolean>
): string {
  const raw = roomStatusOfJob(job, roomStatusByModelKey, modelsByKey)
  if (raw !== 'Unknown') return raw

  const anyJ = job as any
  const phaseLower = String(anyJ?.phase ?? '').trim().toLowerCase()
  const statusLower = String(job.status ?? '').trim().toLowerCase()

  const isActiveRecording =
    !job.endedAt &&
    statusLower === 'running' &&
    (phaseLower === '' || phaseLower === 'recording')

  if (isActiveRecording && growingByJobId?.[job.id]) {
    return 'Public'
  }

  return raw
}

function previewRoomStatusOfJob(
  job: RecordJob,
  roomStatusByModelKey?: Record<string, string>,
  modelsByKey?: Record<string, { roomStatus?: string }>,
  growingByJobId?: Record<string, boolean>
): string {
  const postworkState = getEffectivePostworkState(job)

  if (postworkState === 'running' || postworkState === 'queued') {
    return 'Public'
  }

  return effectiveRoomStatusOfJob(job, roomStatusByModelKey, modelsByKey, growingByJobId)
}

const roomStatusTone = (status: string): string => {
  switch (status) {
    case 'Public':
      return 'bg-emerald-500/15 text-emerald-900 ring-emerald-500/30 dark:bg-emerald-400/10 dark:text-emerald-200 dark:ring-emerald-400/25'
    case 'Private':
      return 'bg-fuchsia-500/15 text-fuchsia-900 ring-fuchsia-500/30 dark:bg-fuchsia-400/10 dark:text-fuchsia-200 dark:ring-fuchsia-400/25'
    case 'Hidden':
      return 'bg-slate-500/15 text-slate-900 ring-slate-500/30 dark:bg-slate-400/10 dark:text-slate-200 dark:ring-slate-400/25'
    case 'Away':
      return 'bg-amber-500/15 text-amber-900 ring-amber-500/30 dark:bg-amber-400/10 dark:text-amber-200 dark:ring-amber-400/25'
    case 'Offline':
      return 'bg-gray-900/5 text-gray-800 ring-gray-900/10 dark:bg-white/10 dark:text-gray-200 dark:ring-white/10'
    case 'Unknown':
    default:
      return 'bg-gray-900/5 text-gray-800 ring-gray-900/10 dark:bg-white/10 dark:text-gray-200 dark:ring-white/10'
  }
}

const toMs = (v: unknown): number => {
  if (typeof v === 'number' && Number.isFinite(v)) {
    return v < 1_000_000_000_000 ? v * 1000 : v
  }
  if (typeof v === 'string') {
    const ms = Date.parse(v)
    return Number.isFinite(ms) ? ms : 0
  }
  if (v instanceof Date) return v.getTime()
  return 0
}

const formatDuration = (ms: number): string => {
  if (!Number.isFinite(ms) || ms <= 0) return '—'
  const total = Math.floor(ms / 1000)
  const h = Math.floor(total / 3600)
  const m = Math.floor((total % 3600) / 60)
  const s = total % 60
  if (h > 0) return `${h}h ${m}m`
  if (m > 0) return `${m}m ${s}s`
  return `${s}s`
}

const runtimeOf = (j: RecordJob, nowMs: number) => {
  const anyJ = j as any

  const start =
    toMs(anyJ.startedAtMs) ||
    toMs(j.startedAt)

  if (!Number.isFinite(start) || start <= 0) return '—'

  const end =
    j.endedAt
      ? (toMs(anyJ.endedAtMs) || toMs(j.endedAt))
      : nowMs

  if (!Number.isFinite(end) || end <= 0) return '—'

  return formatDuration(end - start)
}

const sizeBytesOf = (job: RecordJob): number | null => {
  const anyJob = job as any
  const v = anyJob.sizeBytes ?? anyJob.fileSizeBytes ?? anyJob.bytes ?? anyJob.size ?? null
  return typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : null
}

const formatBytes = (bytes: number | null): string => {
  if (!bytes || !Number.isFinite(bytes) || bytes <= 0) return '—'
  const units = ['B', 'KB', 'MB', 'GB', 'TB']
  let v = bytes
  let i = 0
  while (v >= 1024 && i < units.length - 1) {
    v /= 1024
    i++
  }
  const digits = i === 0 ? 0 : v >= 10 ? 1 : 2
  const s = v.toFixed(digits).replace(/\.0+$/, '')
  return `${s} ${units[i]}`
}

const phaseLabel = (p?: string) => {
  switch ((p ?? '').toLowerCase()) {
    case 'stopping':
      return 'Stoppe Aufnahme…'
    case 'probe':
      return 'Analysiere Datei (Dauer/Streams)…'
    case 'remuxing':
      return 'Konvertiere Container zu MP4…'
    case 'moving':
      return 'Verschiebe nach Done…'
    case 'assets':
      return 'Erstelle Vorschau/Thumbnails…'
    case 'analyze':
      return 'AI analysiert Segmente…'
    case 'postwork':
      return 'Nacharbeiten laufen…'
    default:
      return ''
  }
}

const STOP_RETRY_AFTER_MS = 15_000

const stopRequestedAtMsOf = (job: RecordJob): number => {
  return toMs((job as any)?.stopRequestedAtMs)
}

const isFreshStoppingPhase = (job: RecordJob, nowMs: number): boolean => {
  const phase = String((job as any)?.phase ?? '').trim().toLowerCase()
  if (phase !== 'stopping') return false

  const startedAt = stopRequestedAtMsOf(job)
  return startedAt > 0 && Math.max(0, nowMs - startedAt) < STOP_RETRY_AFTER_MS
}

const canRetryStoppingJob = (job: RecordJob, nowMs: number): boolean => {
  const phase = String((job as any)?.phase ?? '').trim().toLowerCase()
  const status = String((job as any)?.status ?? '').trim().toLowerCase()
  return phase === 'stopping' && status === 'running' && !isFreshStoppingPhase(job, nowMs)
}

const isStopWaitingForJob = (
  job: RecordJob,
  localStopRequested: boolean,
  nowMs: number
): boolean => {
  return localStopRequested || isFreshStoppingPhase(job, nowMs)
}

const isStoppingForUi = (
  job: RecordJob,
  localStopRequested: boolean,
  nowMs: number
): boolean => {
  const phase = String((job as any)?.phase ?? '').trim().toLowerCase()
  const status = String((job as any)?.status ?? '').trim().toLowerCase()
  const isBusyPhase = phase !== '' && phase !== 'recording' && phase !== 'stopping'
  return isBusyPhase || status !== 'running' || isStopWaitingForJob(job, localStopRequested, nowMs)
}

const isTerminalStatus = (status?: unknown) => {
  const s = String(status ?? '').trim().toLowerCase()
  return (
    s === 'stopped' ||
    s === 'finished' ||
    s === 'failed' ||
    s === 'done' ||
    s === 'completed' ||
    s === 'canceled' ||
    s === 'cancelled'
  )
}

const isPostworkJob = (job: RecordJob): boolean => {
  const anyJ = job as any
  const phase = String(anyJ.phase ?? '').trim()
  const pw = anyJ.postWork
  const pwKey = String(anyJ.postWorkKey ?? '').trim()

  if (pwKey) return true
  if (pw && (pw.state === 'queued' || pw.state === 'running')) return true
  if (job.endedAt && phase) return true
  if (phase === 'postwork') return true

  return false
}

const getEffectivePostworkState = (job: RecordJob): 'running' | 'queued' | 'none' => {
  const anyJ = job as any
  const phase = String(anyJ.phase ?? '').trim().toLowerCase()
  const pw = anyJ.postWork
  const pwState = String(pw?.state ?? '').trim().toLowerCase()
  const hasPwKey = String(anyJ.postWorkKey ?? '').trim() !== ''

  if (!isPostworkJob(job)) return 'none'
  if (isTerminalStatus(anyJ?.status)) return 'none'

  if (pwState === 'queued') return 'queued'
  if (pwState === 'running') return 'running'

  if (
    phase === 'postwork' ||
    phase === 'probe' ||
    phase === 'remuxing' ||
    phase === 'moving' ||
    phase === 'assets'
  ) {
    return 'running'
  }

  if (
    typeof pw?.position === 'number' &&
    Number.isFinite(pw.position) &&
    pw.position > 0
  ) {
    return 'queued'
  }

  if (
    typeof pw?.running === 'number' &&
    Number.isFinite(pw.running) &&
    pw.running > 0 &&
    (!Number.isFinite(pw?.position) || pw.position <= 0)
  ) {
    return 'running'
  }

  if (hasPwKey) {
    return 'queued'
  }

  if (anyJ.endedAt || job.endedAt) return 'queued'

  return 'none'
}

function postWorkLabel(
  job: RecordJob,
  override?: { pos?: number; total?: number }
): string {
  const anyJ = job as any
  const pw = anyJ.postWork

  const effectiveState = getEffectivePostworkState(job)

  if (effectiveState === 'running') {
    const running = typeof pw?.running === 'number' && pw.running > 0 ? pw.running : 1
    const maxP = typeof pw?.maxParallel === 'number' ? pw.maxParallel : 0

    return maxP > 0
      ? `Nacharbeiten laufen… (${running}/${maxP} parallel)`
      : 'Nacharbeiten laufen…'
  }

  if (effectiveState === 'queued') {
    const posServer = typeof pw?.position === 'number' ? pw.position : 0
    const waitingServer = typeof pw?.waiting === 'number' ? pw.waiting : 0
    const totalServer = Math.max(waitingServer, posServer)

    const pos =
      typeof override?.pos === 'number' && Number.isFinite(override.pos) && override.pos > 0
        ? override.pos
        : posServer

    const total =
      typeof override?.total === 'number' && Number.isFinite(override.total) && override.total > 0
        ? override.total
        : totalServer

    return pos > 0 && total > 0
      ? `Warte auf Nacharbeiten… ${pos} / ${total}`
      : 'Warte auf Nacharbeiten…'
  }

  return 'Warte auf Nacharbeiten…'
}

export function PendingPreviewImage({
  src,
  alt,
  blur,
  className,
}: {
  src: string
  alt: string
  blur?: boolean
  className?: string
}) {
  const rootRef = useRef<HTMLDivElement | null>(null)
  const [inView, setInView] = useState(false)
  const [nonce, setNonce] = useState(0)
  const [imgError, setImgError] = useState(false)

  useEffect(() => {
    const el = rootRef.current
    if (!el) return

    const obs = new IntersectionObserver(
      (entries) => {
        const entry = entries[0]
        setInView(Boolean(entry?.isIntersecting || entry?.intersectionRatio > 0))
      },
      { root: null, threshold: 0.01 }
    )

    obs.observe(el)
    return () => obs.disconnect()
  }, [])

  useEffect(() => {
    if (!inView) return

    const id = window.setInterval(() => {
      setNonce((n) => n + 1)
    }, 20000)

    return () => window.clearInterval(id)
  }, [inView, src])

  useEffect(() => {
    setImgError(false)
  }, [src])

  const finalSrc = useMemo(() => {
    if (!inView) return ''

    const s = String(src ?? '').trim()
    if (!s) return ''

    return s.includes('?') ? `${s}&v=${nonce}` : `${s}?v=${nonce}`
  }, [src, nonce, inView])

  return (
    <div
      ref={rootRef}
      className={className}
    >
      {inView && !imgError && finalSrc ? (
        <img
          src={finalSrc}
          alt={alt}
          className={[
            'block h-full w-full object-cover',
            blur ? 'blur-md' : '',
          ].join(' ')}
          loading="lazy"
          decoding="async"
          referrerPolicy="no-referrer"
          onError={() => setImgError(true)}
        />
      ) : (
        <div
          className={[
            'grid h-full w-full place-items-center bg-gray-100 text-sm text-gray-500 dark:bg-white/10 dark:text-gray-300',
            blur ? 'blur-md' : '',
          ].join(' ')}
        >
          ⏳
        </div>
      )}
    </div>
  )
}

export default function DownloadsCardRow({
  r,
  nowMs,
  blurPreviews,
  modelsByKey,
  roomStatusByModelKey,
  stopRequestedIds,
  growingByJobId,
  postworkInfoOf,
  markStopRequested,
  onOpenPlayer,
  onStopJob,
  onRemoveQueuedPostworkJob,
  onToggleFavorite,
  onToggleLike,
  onToggleWatch,
}: Props) {
  if (r.kind === 'pending') {
    const p = r.pending
    const name = pendingModelName(p)
    const url = pendingUrl(p)
    const show = normalizeRoomStatus(p.currentShow)

    const pendingKey = String(p.modelKey ?? name).trim().toLowerCase()
    const pendingFlags = pendingKey ? modelsByKey[pendingKey] : undefined

    const img =
      pendingImageUrl(p) ||
      String(pendingFlags?.imageUrl ?? '').trim()

    const pendingActionJob = {
      id: `pending:${pendingKey || name}`,
      output: `${name}_pending.mp4`,
      sourceUrl: url,
      status: 'pending',
      endedAt: null,
    } as unknown as RecordJob

    const isFav = Boolean(pendingFlags?.favorite)
    const isLiked = pendingFlags?.liked === true
    const isWatching = Boolean(pendingFlags?.watching)

    return (
      <div
        className="
          group relative isolate overflow-hidden rounded-2xl
          border border-gray-200/90 bg-white shadow-sm
          ring-1 ring-black/5
          transition-all hover:-translate-y-0.5 hover:shadow-md active:translate-y-0
          dark:border-white/10 dark:bg-gray-950
        "
      >
        <div className="pointer-events-none absolute inset-0 bg-gradient-to-br from-gray-50/90 via-white to-gray-50/70 dark:from-white/10 dark:via-transparent dark:to-white/5" />
        <div className="pointer-events-none absolute -inset-10 opacity-0 blur-2xl transition-opacity duration-300 group-hover:opacity-100 bg-white/25 dark:bg-white/5" />

        <div className="relative p-3">
          <div className="flex items-start gap-3">
            <div
              className="
                relative shrink-0 overflow-hidden rounded-lg
                w-[112px] h-[64px]
                bg-gray-100 ring-1 ring-black/5
                dark:bg-white/10 dark:ring-white/10
              "
            >
              {img ? (
                <PendingPreviewImage
                  src={img}
                  alt={name}
                  blur={blurPreviews}
                  className="h-full w-full"
                />
              ) : (
                <div className="grid h-full w-full place-items-center text-sm text-gray-500 dark:text-gray-300">
                  ⏳
                </div>
              )}
            </div>

            <div className="min-w-0 flex-1">
              <div className="flex items-start justify-between gap-3">
                <div className="min-w-0 flex-1">
                  <div className="flex min-w-0 items-center gap-1.5">
                    <div
                      className="min-w-0 truncate text-base font-semibold text-gray-900 dark:text-white"
                      title={name}
                    >
                      {name}
                    </div>

                    <ModelGenderIcon
                      gender={pendingFlags?.gender}
                      className="shrink-0"
                    />

                    <CountryFlag
                      country={pendingFlags?.country}
                      size="sm"
                      className="shrink-0"
                    />
                  </div>

                  <div className="mt-0.5 truncate text-xs text-gray-600 dark:text-gray-300">
                    Warte auf Online-Status…
                  </div>
                </div>

                <div className="shrink-0 flex items-start">
                  <span
                    className={[
                      'inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-semibold ring-1',
                      roomStatusTone(show),
                    ].join(' ')}
                    title={show}
                  >
                    {show}
                  </span>
                </div>
              </div>

              {url ? (
                <a
                  href={url}
                  target="_blank"
                  rel="noreferrer"
                  className="mt-1 block truncate text-xs text-indigo-600 hover:underline dark:text-indigo-400"
                  onClick={(e) => e.stopPropagation()}
                  title={url}
                >
                  {url}
                </a>
              ) : null}
            </div>
          </div>

          <div className="mt-3 flex items-center gap-3 border-t border-white/30 pt-3 dark:border-white/10">
            <div
              className="flex items-center gap-1"
              onClick={(e) => e.stopPropagation()}
              onMouseDown={(e) => e.stopPropagation()}
            >
              <RecordJobActions
                job={pendingActionJob}
                variant="table"
                busy={false}
                isFavorite={isFav}
                isLiked={isLiked}
                isWatching={isWatching}
                onToggleFavorite={onToggleFavorite}
                onToggleLike={onToggleLike}
                onToggleWatch={onToggleWatch}
                order={['watch', 'favorite', 'like', 'details']}
                className="flex items-center gap-1"
              />
            </div>

            <div
              className="ml-auto flex items-center justify-end gap-2"
              onClick={(e) => e.stopPropagation()}
              onMouseDown={(e) => e.stopPropagation()}
            >
              <span className="rounded-full bg-gray-900/5 px-2 py-0.5 text-[11px] font-medium text-gray-900 dark:bg-white/10 dark:text-gray-200 whitespace-nowrap">
                Wartend
              </span>

              <div className="hidden sm:block">
                <Button
                  size="sm"
                  variant="primary"
                  color="red"
                  className="shrink-0"
                  disabled
                >
                  Wartend
                </Button>
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }

  const j = r.job
  const name = modelNameFromOutput(j.output)
  const file = baseName(j.output || '')

  const phase = String((j as any).phase ?? '').trim()
  const phaseLower = phase.toLowerCase()
  const isRecording = phaseLower === 'recording'

  const isStopRequested = Boolean(stopRequestedIds[j.id])

  const postworkState = getEffectivePostworkState(j)
  const isQueuedPostwork = postworkState === 'queued'

  const canRetryStop = canRetryStoppingJob(j, nowMs) && !isStopRequested
  const isStopping = isStoppingForUi(j, isStopRequested, nowMs)

  const showRemoveQueuedButton = isQueuedPostwork
  const disableStopButton = showRemoveQueuedButton ? false : isStopping
  const actionsBusy = showRemoveQueuedButton ? false : isStopping

  const roomStatus = effectiveRoomStatusOfJob(
    j,
    roomStatusByModelKey,
    modelsByKey,
    growingByJobId
  )

  let phaseText = ''

  if (phaseLower === 'recording') {
    phaseText = 'Recording läuft…'
  } else if (isPostworkJob(j)) {
    const pwState = getEffectivePostworkState(j)

    if (phaseLower === 'probe' || phaseLower === 'remuxing' || phaseLower === 'moving' || phaseLower === 'assets') {
      phaseText = phaseLabel(phase) || phase
    } else if (pwState === 'queued') {
      phaseText = postWorkLabel(j, postworkInfoOf(j))
    } else if (pwState === 'running') {
      phaseText = phaseLabel(phase) || postWorkLabel(j, postworkInfoOf(j))
    }
  } else {
    if (phaseLower === 'stopping' && canRetryStop) {
      phaseText = 'Stop dauert zu lange - erneut stoppen'
    } else {
      phaseText = phase ? (phaseLabel(phase) || phase) : ''
    }
  }

  const progressLabel = phaseText || roomStatus

  const progress = Number((j as any).progress ?? 0)
  const showBar =
    !isRecording &&
    Number.isFinite(progress) &&
    progress > 0 &&
    progress < 100

  const showIndeterminate =
    !isRecording &&
    !showBar &&
    Boolean(phase) &&
    (!Number.isFinite(progress) || progress <= 0 || progress >= 100)

  const modelKey = modelKeyFromJob(j)
  const flags = modelKey ? modelsByKey[modelKey] : undefined
  const isFav = Boolean(flags?.favorite)
  const isLiked = flags?.liked === true
  const isWatching = Boolean(flags?.watching)
  const directPreviewSrc = previewInitialSrcOfJob(j, modelsByKey)

  return (
    <div
      className="
        group relative isolate overflow-hidden rounded-2xl
        border border-gray-200/90 bg-white shadow-sm
        ring-1 ring-black/5
        transition-all hover:-translate-y-0.5 hover:shadow-md active:translate-y-0
        dark:border-white/10 dark:bg-gray-950
      "
      onClick={() => onOpenPlayer(j)}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') onOpenPlayer(j)
      }}
    >
      <div className="pointer-events-none absolute inset-0 bg-gradient-to-br from-gray-50/90 via-white to-gray-50/70 dark:from-white/10 dark:via-transparent dark:to-white/5" />
      <div className="pointer-events-none absolute -inset-10 opacity-0 blur-2xl transition-opacity duration-300 group-hover:opacity-100 bg-white/25 dark:bg-white/5" />

      <div className="relative p-3">
        <div className="flex items-start gap-3">
          <div
            className="
              relative
              shrink-0 overflow-hidden rounded-lg
              w-[112px] h-[64px]
              bg-gray-100 ring-1 ring-black/5
              dark:bg-white/10 dark:ring-white/10
            "
          >
            <LazyMountWhenVisible
              className="w-full h-full"
              placeholder={<div className="h-full w-full bg-gray-100 dark:bg-white/10" />}
            >
              <ModelPreview
                jobId={j.id}
                initialSrc={directPreviewSrc || undefined}
                enableHoverLive={false}
                blur={blurPreviews}
                roomStatus={previewRoomStatusOfJob(
                  j,
                  roomStatusByModelKey,
                  modelsByKey,
                  growingByJobId
                )}
                fallbackSrc={`/api/preview?id=${encodeURIComponent(j.id)}&fallbackOnly=1`}
                className="w-full h-full"
              />
            </LazyMountWhenVisible>
          </div>

          <div className="min-w-0 flex-1">
            <div className="flex items-start justify-between gap-3">
              <div className="min-w-0 flex-1">
                <div className="flex min-w-0 items-center gap-1.5">
                  <div
                    className="min-w-0 truncate text-base font-semibold text-gray-900 dark:text-white"
                    title={name}
                  >
                    {name}
                  </div>

                  <ModelGenderIcon
                    gender={flags?.gender}
                    className="shrink-0"
                  />

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

                <div
                  className="mt-0.5 truncate text-xs text-gray-600 dark:text-gray-300"
                  title={j.output}
                >
                  {file || '—'}
                </div>
              </div>

              <div className="shrink-0 flex items-start">
                <span
                  className={[
                    'inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-semibold ring-1',
                    roomStatusTone(roomStatus),
                  ].join(' ')}
                  title={roomStatus}
                >
                  {roomStatus}
                </span>
              </div>
            </div>

            {j.sourceUrl ? (
              <a
                href={j.sourceUrl}
                target="_blank"
                rel="noreferrer"
                className="mt-1 block truncate text-xs text-indigo-600 hover:underline dark:text-indigo-400"
                onClick={(e) => e.stopPropagation()}
                title={j.sourceUrl}
              >
                {j.sourceUrl}
              </a>
            ) : null}
          </div>
        </div>

        {(showBar || showIndeterminate) ? (
          <div className="mt-3">
            {showBar ? (
              <ProgressBar
                label={progressLabel}
                value={Math.max(0, Math.min(100, progress))}
                showPercent
                size="sm"
                className="w-full"
              />
            ) : (
              <ProgressBar
                label={progressLabel}
                indeterminate
                size="sm"
                className="w-full"
              />
            )}
          </div>
        ) : null}

        <div className="mt-3 flex items-center gap-3 border-t border-white/30 pt-3 dark:border-white/10">
          <div
            className="flex items-center gap-1"
            onClick={(e) => e.stopPropagation()}
            onMouseDown={(e) => e.stopPropagation()}
          >
            <RecordJobActions
              job={j}
              variant="table"
              busy={actionsBusy}
              isFavorite={isFav}
              isLiked={isLiked}
              isWatching={isWatching}
              onToggleFavorite={onToggleFavorite}
              onToggleLike={onToggleLike}
              onToggleWatch={onToggleWatch}
              order={['watch', 'favorite', 'like', 'details']}
              className="flex items-center gap-1"
            />
          </div>

          <div
            className="ml-auto flex min-w-0 self-center items-center justify-end gap-2"
            onClick={(e) => e.stopPropagation()}
            onMouseDown={(e) => e.stopPropagation()}
          >
            <div className="flex items-center justify-end gap-2 text-right">
              <span className="rounded-full bg-gray-900/5 px-2 py-0.5 text-[11px] font-medium text-gray-900 dark:bg-white/10 dark:text-gray-200 whitespace-nowrap">
                {runtimeOf(j, nowMs)}
              </span>
              <span className="rounded-full bg-gray-900/5 px-2 py-0.5 text-[11px] font-medium text-gray-900 dark:bg-white/10 dark:text-gray-200 whitespace-nowrap">
                {formatBytes(sizeBytesOf(j))}
              </span>
            </div>

            <div className="hidden sm:block">
              <Button
                size="sm"
                variant="primary"
                color={isQueuedPostwork ? 'red' : 'indigo'}
                disabled={disableStopButton}
                className="shrink-0"
                onClick={async (e) => {
                  e.stopPropagation()

                  if (showRemoveQueuedButton) {
                    await onRemoveQueuedPostworkJob?.(j.id)
                    return
                  }

                  if (isStopping) return
                  markStopRequested(j.id)
                  await onStopJob(j.id)
                }}
              >
                {showRemoveQueuedButton
                  ? 'Entfernen'
                  : canRetryStop
                    ? 'Stop erneut'
                    : (isStopping ? 'Stoppe…' : 'Stop')}
              </Button>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}
