import { useEffect, useState, useCallback, useRef, useMemo } from 'react'
import { Link } from 'react-router-dom'
import {
  LayoutGrid,
  List,
  MoreVertical,
  X,
  Globe,
  Users,
  Lock,
  CheckCircle2,
  Archive,
  CircleDot,
  FilePenLine,
  Copyright,
  Image,
  Video,
  FileText,
  File,
  Upload,
  ScrollText,
} from 'lucide-react'
import { useAuth } from '../context/AuthContext'
import api from '../utils/api'
import { activeClubMemberships } from '../utils/activeClub'
import { resolveMediaAssetFileUrl } from '../utils/exerciseMediaUrl'
import RightsDeclarationDialog from '../components/RightsDeclarationDialog'
import ReportContentModal from '../components/ReportContentModal'
import MediaPreviewModal from '../components/MediaPreviewModal'

const LC_OPTIONS = [
  { value: 'active', label: 'Aktiv' },
  { value: 'trash_soft', label: 'Papierkorb (1)' },
  { value: 'trash_hidden', label: 'Ausgeblendet (2)' },
  { value: 'all', label: 'Alle' },
]

const VIS_OPTIONS = [
  { value: 'private', label: 'Privat' },
  { value: 'club', label: 'Verein' },
  { value: 'official', label: 'Offiziell' },
]

const MEDIA_KIND_OPTIONS = [
  { value: 'all', label: 'Alle Typen' },
  { value: 'image', label: 'Bild' },
  { value: 'video', label: 'Video' },
  { value: 'pdf', label: 'PDF' },
  { value: 'other', label: 'Sonstiges' },
]

const LC_STATUS_LABELS = {
  active: 'Aktiv',
  trash_soft: 'Papierkorb (1)',
  trash_hidden: 'Ausgeblendet (2)',
}

function visibilityUiLabel(v) {
  const o = VIS_OPTIONS.find((x) => x.value === (v || '').toLowerCase())
  return o ? o.label : v || '—'
}

function MediaCardScopeStatus({ visibility, lifecycleState }) {
  const v = (visibility || 'private').toLowerCase()
  const lc = (lifecycleState || 'active').toLowerCase()
  const visLabel = visibilityUiLabel(v)
  const lcLabel = LC_STATUS_LABELS[lc] || lcLabelFromOptions(lc)
  const tip = `${visLabel} · ${lcLabel}`
  let VisIcon = Lock
  if (v === 'official') VisIcon = Globe
  else if (v === 'club') VisIcon = Users
  let LcIcon = FilePenLine
  if (lc === 'active') LcIcon = CheckCircle2
  else if (lc === 'archived' || lc === 'trash_hidden') LcIcon = Archive
  else if (lc === 'in_review' || lc === 'trash_soft') LcIcon = CircleDot
  return (
    <div
      className="exercise-card__meta-compact"
      title={tip}
      aria-label={`Sichtbarkeit: ${visLabel}. Lebenszyklus: ${lcLabel}.`}
    >
      <span className="exercise-card__meta-glyph">
        <VisIcon size={15} strokeWidth={2} aria-hidden />
      </span>
      <span className="exercise-card__meta-sep" aria-hidden>
        ·
      </span>
      <span className="exercise-card__meta-glyph">
        <LcIcon size={15} strokeWidth={2} aria-hidden />
      </span>
    </div>
  )
}

function lcLabelFromOptions(code) {
  const o = LC_OPTIONS.find((x) => x.value === code)
  return o ? o.label : code
}

function lcLabel(code) {
  return lcLabelFromOptions(code)
}

function parseTagsInput(s) {
  return String(s || '')
    .split(/[,;\n]+/)
    .map((x) => x.trim())
    .filter(Boolean)
}

function MediaUsageBlock({ usage, compact }) {
  const u = usage || { exercises: [], training_units: [] }
  const ex = u.exercises || []
  const tus = u.training_units || []
  if (!ex.length && !tus.length)
    return <span className="media-library__hint">{compact ? '—' : 'Noch in keiner Übung / Einheit verknüpft.'}</span>
  return (
    <div className="media-library__usage-links">
      {ex.length ? (
        <div>
          <strong>Übungen</strong>{' '}
          {ex.map((e) => (
            <Link key={e.id} to={`/exercises/${e.id}`} title={e.title}>
              {e.title.length > (compact ? 18 : 40) ? `${e.title.slice(0, compact ? 18 : 40)}…` : e.title}
            </Link>
          ))}
        </div>
      ) : null}
      {tus.length ? (
        <div style={{ marginTop: ex.length ? 6 : 0 }}>
          <strong>Trainings­einheiten</strong>{' '}
          {tus.map((t) => {
            const label =
              [t.planned_date, (t.group_name || '').trim()].filter(Boolean).join(' · ') || `Einheit #${t.id}`
            const short = label.length > (compact ? 20 : 36) ? `${label.slice(0, compact ? 20 : 36)}…` : label
            return (
              <Link key={t.id} to={`/planning?unit=${t.id}`} title={label}>
                {short}
              </Link>
            )
          })}
        </div>
      ) : null}
    </div>
  )
}

function uploaderLabel(it, viewer) {
  if (!viewer?.show_uploader_meta) return null
  const n = (it.uploader_name || '').trim()
  const e = (it.uploader_email || '').trim()
  if (n) return n
  if (e) return e
  return it.uploaded_by_profile_id != null ? `Profil #${it.uploaded_by_profile_id}` : '—'
}

function MediaThumb({ mediaId, mimeType }) {
  const url = resolveMediaAssetFileUrl(mediaId)
  const mime = (mimeType || '').toLowerCase()
  /* iPhone-Fotos: Browser-Vorschau oft nicht nutzbar */
  if (mime.includes('heic') || mime.includes('heif')) {
    return <div className="media-library__thumb-ph">HEIC</div>
  }
  if (mime.startsWith('video/')) {
    return (
      <video
        className="media-library__thumb-video"
        src={url}
        muted
        playsInline
        preload="metadata"
        onLoadedMetadata={(e) => {
          const el = e.target
          try {
            const d = el.duration
            el.currentTime = Number.isFinite(d) && d > 0 ? Math.min(0.05, d * 0.01) : 0.05
          } catch {
            /* ignore */
          }
        }}
      />
    )
  }
  if (mime.startsWith('image/')) {
    return (
      <img
        className="media-library__thumb-img"
        src={url}
        alt=""
        loading="lazy"
        onError={(e) => {
          e.target.style.display = 'none'
        }}
      />
    )
  }
  if (mime.includes('pdf')) {
    return <div className="media-library__thumb-ph">PDF</div>
  }
  return <div className="media-library__thumb-ph">◆</div>
}

function previewDisplayKind(mimeType) {
  const m = (mimeType || '').toLowerCase()
  if (m.startsWith('image/')) return 'image'
  if (m.startsWith('video/')) return 'video'
  if (m.includes('pdf')) return 'pdf'
  return 'other'
}

const MEDIA_KIND_LABELS = {
  image: 'Bild',
  video: 'Video',
  pdf: 'PDF',
  other: 'Sonstiges',
}

function MediaTypeGlyph({ mimeType, compact }) {
  const kind = previewDisplayKind(mimeType)
  const label = MEDIA_KIND_LABELS[kind] || 'Medium'
  let Icon = File
  if (kind === 'image') Icon = Image
  else if (kind === 'video') Icon = Video
  else if (kind === 'pdf') Icon = FileText
  const sz = compact ? 12 : 14
  return (
    <span
      className={`media-library__card-type${compact ? ' media-library__card-type--compact' : ' media-library__card-type--thumb-bl'}`}
      title={label}
      aria-label={`Medientyp: ${label}`}
    >
      <Icon size={sz} strokeWidth={2} aria-hidden />
    </span>
  )
}

function actionTypeLabel(at) {
  const MAP = {
    upload: 'Erstupload',
    promote_club: 'Promotion → Verein',
    promote_official: 'Promotion → Offiziell',
    re_declaration: 'Nachdeklaration',
    legacy_re_declaration: 'Altbestand nachdeklariert',
    correction: 'Korrektur',
  }
  return MAP[at] || at
}

function eventTypeLabel(et) {
  const MAP = {
    visibility_change: 'Sichtbarkeit geändert',
    copyright_change: 'Copyright geändert',
    metadata_change: 'Metadaten geändert',
    lifecycle_change: 'Lifecycle geändert',
    legal_hold_set: 'Sofortsperre gesetzt',
    legal_hold_released: 'Sofortsperre aufgehoben',
    content_report_filed: 'Inhaltsmeldung',
  }
  return MAP[et] || et
}

const LEGAL_HOLD_REASON_LABELS = {
  rights_dispute: 'Rechtsstreit',
  consent_withdrawn: 'Einwilligung widerrufen',
  privacy_complaint: 'Datenschutzbeschwerde',
  copyright_complaint: 'Urheberrechtsbeschwerde',
  youth_protection: 'Jugendschutz',
  illegal_content: 'Illegaler Inhalt',
  other: 'Sonstige',
}

const LEGAL_HOLD_REASON_CODES = Object.keys(LEGAL_HOLD_REASON_LABELS)

function visLabel(v) {
  if (v === 'private') return 'Privat'
  if (v === 'club') return 'Verein'
  if (v === 'official') return 'Offiziell'
  return v || '—'
}

function JournalBoolField({ label, val, context }) {
  if (val === null || val === undefined) return null
  return (
    <div className="media-library__journal-field">
      <span className="media-library__journal-field-label">{label}</span>
      <span className={`media-library__journal-field-val${val ? ' media-library__journal-field-val--yes' : ' media-library__journal-field-val--no'}`}>
        {val ? 'Ja' : 'Nein'}
      </span>
      {context ? <span className="media-library__journal-field-context">„{context}"</span> : null}
    </div>
  )
}

export default function MediaLibraryPage() {
  const { user } = useAuth()
  const isPlatformAdmin = user?.role === 'admin' || user?.role === 'superadmin'
  const isSuperadmin = user?.role === 'superadmin'
  const hasClubOrgAdmin = activeClubMemberships(user?.clubs).some((c) => (c.roles || []).includes('club_admin'))

  const archiveVisOptions = useMemo(
    () => VIS_OPTIONS.filter((o) => o.value !== 'official' || isSuperadmin),
    [isSuperadmin],
  )

  const [lifecycle, setLifecycle] = useState('active')
  const [q, setQ] = useState('')
  const [items, setItems] = useState([])
  const [viewer, setViewer] = useState(null)
  const [clubs, setClubs] = useState([])
  const [viewMode, setViewMode] = useState('grid')
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState('')
  const [selected, setSelected] = useState(() => new Set())
  const [modal, setModal] = useState(null)
  const [modalDraft, setModalDraft] = useState(null)
  const [bulkOpen, setBulkOpen] = useState(false)
  const [bulkCopyright, setBulkCopyright] = useState('')
  const [bulkVis, setBulkVis] = useState('private')
  const [bulkClubId, setBulkClubId] = useState('')
  const [bulkApplyVis, setBulkApplyVis] = useState(false)
  const [busy, setBusy] = useState(false)
  const [preview, setPreview] = useState(null)
  const [reportTarget, setReportTarget] = useState(null)
  const [mediaKind, setMediaKind] = useState('all')
  const [filterClubId, setFilterClubId] = useState('')
  const [filterUploaderId, setFilterUploaderId] = useState('')
  const [uploaderFilterOptions, setUploaderFilterOptions] = useState([])
  const bulkFileInputRef = useRef(null)
  const [uploadVis, setUploadVis] = useState('private')
  const [uploadClubId, setUploadClubId] = useState('')
  const [uploadBusy, setUploadBusy] = useState(false)
  const [uploadSummary, setUploadSummary] = useState('')
  // P-06: Rechte-Dialog (Upload)
  const [rightsDialogOpen, setRightsDialogOpen] = useState(false)
  const [pendingUploadFiles, setPendingUploadFiles] = useState(null)
  // P-06: Rechte-Dialog (Sichtbarkeits-Promotion)
  const [rightsUpgradeDialogOpen, setRightsUpgradeDialogOpen] = useState(false)
  const [rightsUpgradeMode, setRightsUpgradeMode] = useState('promotion')
  const [pendingPatchBody, setPendingPatchBody] = useState(null)
  const [journalModal, setJournalModal] = useState(null)
  const [journalLoading, setJournalLoading] = useState(false)
  const [journalCorrectionOpen, setJournalCorrectionOpen] = useState(false)
  const [journalCorrectionDraft, setJournalCorrectionDraft] = useState(null)
  const [journalCorrectionBusy, setJournalCorrectionBusy] = useState(false)
  // P-11: Legal Hold Dialog
  const [legalHoldDialog, setLegalHoldDialog] = useState(null) // { mode: 'set'|'release', assetId, assetName }
  const [legalHoldReasonCode, setLegalHoldReasonCode] = useState('rights_dispute')
  const [legalHoldReasonNote, setLegalHoldReasonNote] = useState('')
  const [legalHoldReleaseNote, setLegalHoldReleaseNote] = useState('')
  const [legalHoldBusy, setLegalHoldBusy] = useState(false)
  const mediaListFetchSeqRef = useRef(0)
  const gridTopAnchorRef = useRef(null)

  const modalVisibilityOptions = useMemo(() => {
    if (!modalDraft) return archiveVisOptions
    const o = [...archiveVisOptions]
    if (!o.some((x) => x.value === modalDraft.visibility)) {
      o.push({
        value: modalDraft.visibility,
        label: visibilityUiLabel(modalDraft.visibility),
      })
    }
    return o
  }, [archiveVisOptions, modalDraft])

  useEffect(() => {
    if (!isSuperadmin) return
    const cid = user?.effective_club_id ?? user?.active_club_id
    if (cid == null || cid === '') return
    setUploadClubId(String(cid))
  }, [isSuperadmin, user?.effective_club_id, user?.active_club_id])

  const loadClubs = useCallback(async () => {
    try {
      const c = await api.listClubs()
      setClubs(Array.isArray(c) ? c : [])
    } catch {
      setClubs([])
    }
  }, [])

  useEffect(() => {
    loadClubs()
  }, [loadClubs])

  const loadItems = useCallback(async () => {
    const seq = ++mediaListFetchSeqRef.current
    setLoading(true)
    setError('')
    try {
      const res = await api.listMediaAssets({
        lifecycle,
        q: q.trim(),
        limit: 100,
        offset: 0,
        media_kind: mediaKind,
        include_filter_meta: true,
        ...(isSuperadmin && filterClubId
          ? { club_id: Number(filterClubId) }
          : {}),
        ...(filterUploaderId && viewer?.show_uploader_meta
          ? { uploaded_by: Number(filterUploaderId) }
          : {}),
      })
      if (seq !== mediaListFetchSeqRef.current) return
      setItems(res.items || [])
      setViewer(res.viewer || null)
      if (res.filter_meta?.uploaders?.length) {
        setUploaderFilterOptions(res.filter_meta.uploaders)
      }
      setSelected(new Set())
    } catch (e) {
      if (seq !== mediaListFetchSeqRef.current) return
      setError(e.message || String(e))
    } finally {
      if (seq === mediaListFetchSeqRef.current) setLoading(false)
    }
  }, [lifecycle, q, mediaKind, filterClubId, filterUploaderId, isSuperadmin, viewer?.show_uploader_meta])

  useEffect(() => {
    const t = setTimeout(() => {
      loadItems()
    }, 320)
    return () => clearTimeout(t)
  }, [lifecycle, q, mediaKind, filterClubId, filterUploaderId, loadItems])

  useEffect(() => {
    if (!preview) return
    const onKey = (e) => {
      if (e.key === 'Escape') setPreview(null)
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [preview])

  const toggleSel = (id) => {
    setSelected((prev) => {
      const n = new Set(prev)
      if (n.has(id)) n.delete(id)
      else n.add(id)
      return n
    })
  }

  const selectAll = () => {
    if (selected.size === items.length) setSelected(new Set())
    else setSelected(new Set(items.map((x) => x.id)))
  }

  const openEdit = (it) => {
    setPreview(null)
    const p = it.permissions || {}
    setModal(it)
    setModalDraft({
      display_name: it.original_filename != null ? String(it.original_filename) : '',
      copyright_notice: it.copyright_notice != null ? String(it.copyright_notice) : '',
      tags_input: Array.isArray(it.tags) ? it.tags.join(', ') : '',
      visibility: (it.visibility || 'private').toLowerCase(),
      club_id: it.club_id != null ? String(it.club_id) : '',
      superTarget: 'active',
    })
  }

  const closeModal = () => {
    setModal(null)
    setModalDraft(null)
  }

  const openJournal = async (it) => {
    setJournalLoading(true)
    try {
      const data = await api.getMediaAssetJournal(it.id)
      setJournalModal(data)
    } catch (e) {
      alert(e.message || String(e))
    } finally {
      setJournalLoading(false)
    }
  }

  const openLegalHoldSet = (it) => {
    setLegalHoldReasonCode('rights_dispute')
    setLegalHoldReasonNote('')
    setLegalHoldDialog({ mode: 'set', assetId: it.id, assetName: it.original_filename || `#${it.id}` })
  }

  const openLegalHoldRelease = (it) => {
    setLegalHoldReleaseNote('')
    setLegalHoldDialog({ mode: 'release', assetId: it.id, assetName: it.original_filename || `#${it.id}` })
  }

  const submitLegalHold = async () => {
    if (!legalHoldDialog) return
    if (legalHoldDialog.mode === 'set') {
      if (!legalHoldReasonNote.trim() || legalHoldReasonNote.trim().length < 5) {
        alert('Bitte gib eine Begründung ein (mind. 5 Zeichen).')
        return
      }
    } else {
      if (!legalHoldReleaseNote.trim() || legalHoldReleaseNote.trim().length < 5) {
        alert('Bitte gib eine Freigabe-Begründung ein (mind. 5 Zeichen).')
        return
      }
    }
    setLegalHoldBusy(true)
    try {
      if (legalHoldDialog.mode === 'set') {
        await api.setMediaAssetLegalHold(legalHoldDialog.assetId, legalHoldReasonCode, legalHoldReasonNote)
      } else {
        await api.releaseMediaAssetLegalHold(legalHoldDialog.assetId, legalHoldReleaseNote)
      }
      setLegalHoldDialog(null)
      setModal(null)
      setModalDraft(null)
      await loadItems()
    } catch (e) {
      alert(e.message || String(e))
    } finally {
      setLegalHoldBusy(false)
    }
  }

  function parseApiErrorCode(msg) {
    try {
      const d = JSON.parse(msg)
      if (d && d.code) return d
    } catch { /* not JSON */ }
    return null
  }

  const saveModal = async () => {
    if (!modal || !modalDraft) return
    const p = modal.permissions || {}
    const body = {}
    if (p.edit_metadata) {
      body.original_filename = modalDraft.display_name
      body.copyright_notice = modalDraft.copyright_notice
      body.tags = parseTagsInput(modalDraft.tags_input)
    }
    if (p.change_visibility) {
      body.visibility = modalDraft.visibility
      if (modalDraft.visibility === 'club' || (modalDraft.visibility === 'private' && isPlatformAdmin)) {
        const cid = Number(modalDraft.club_id)
        if (!cid) {
          alert('Bitte einen Verein wählen.')
          return
        }
        body.club_id = cid
      }
    }
    if (!Object.keys(body).length) return
    setBusy(true)
    try {
      await api.patchMediaAsset(modal.id, body)
      closeModal()
      await loadItems()
    } catch (e) {
      const parsed = parseApiErrorCode(e.message)
      if (parsed && (parsed.code === 'RIGHTS_SCOPE_INSUFFICIENT' || parsed.code === 'LEGACY_REDECLARATION_REQUIRED')) {
        setPendingPatchBody(body)
        setRightsUpgradeMode(parsed.code === 'LEGACY_REDECLARATION_REQUIRED' ? 'redeclaration' : 'promotion')
        setRightsUpgradeDialogOpen(true)
        return
      }
      alert(e.message || String(e))
    } finally {
      setBusy(false)
    }
  }

  const doSaveWithRightsDecl = async (decl) => {
    setRightsUpgradeDialogOpen(false)
    if (!modal || !pendingPatchBody) return
    const body = { ...pendingPatchBody, ...decl }
    setPendingPatchBody(null)
    setBusy(true)
    try {
      await api.patchMediaAsset(modal.id, body)
      closeModal()
      await loadItems()
    } catch (e) {
      alert(e.message || String(e))
    } finally {
      setBusy(false)
    }
  }

  const runLc = async (id, action, confirmMsg, extra = {}) => {
    if (confirmMsg && !window.confirm(confirmMsg)) return
    setBusy(true)
    try {
      await api.postMediaAssetLifecycle(id, action, extra)
      await loadItems()
      closeModal()
    } catch (e) {
      alert(e.message || String(e))
    } finally {
      setBusy(false)
    }
  }

  const runBulkPatch = async () => {
    const ids = [...selected]
    if (!ids.length) return
    const body = { media_asset_ids: ids }
    let has = false
    if (bulkCopyright.trim()) {
      body.copyright_notice = bulkCopyright.trim()
      has = true
    }
    if (bulkApplyVis) {
      body.visibility = bulkVis
      if (bulkVis === 'club' || (bulkVis === 'private' && isPlatformAdmin)) {
        const cid = Number(bulkClubId)
        if (!cid) {
          alert('Bitte einen Verein wählen.')
          return
        }
        body.club_id = cid
      }
      has = true
    }
    if (!has) {
      alert('Mindestens Copyright ausfüllen oder „Sichtbarkeit ändern“ aktivieren.')
      return
    }
    setBusy(true)
    try {
      const res = await api.bulkPatchMediaAssets(body)
      if (res.failed_count) {
        alert(
          `${res.updated_count} aktualisiert, ${res.failed_count} fehlgeschlagen. Erste Meldung: ${res.failed[0]?.detail || ''}`,
        )
      }
      setBulkOpen(false)
      await loadItems()
    } catch (e) {
      alert(e.message || String(e))
    } finally {
      setBusy(false)
    }
  }

  const runBulkLifecycle = async (action) => {
    const ids = [...selected]
    if (!ids.length) return
    setBusy(true)
    try {
      const res = await api.bulkMediaLifecycle({ media_asset_ids: ids, action })
      if (res.failed_count) {
        alert(
          `${res.updated_count} OK, ${res.failed_count} fehlgeschlagen: ${res.failed[0]?.detail || ''}`,
        )
      }
      await loadItems()
    } catch (e) {
      alert(e.message || String(e))
    } finally {
      setBusy(false)
    }
  }

  const selCount = selected.size

  const onBulkArchiveFiles = (e) => {
    const fl = e.target.files
    if (!fl?.length) return
    const list = Array.from(fl)
    e.target.value = ''
    if (uploadVis === 'club' && !Number(uploadClubId)) {
      window.alert('Bitte einen Verein für die Sichtbarkeit „Verein” wählen.')
      return
    }
    if (uploadVis === 'private' && isPlatformAdmin && !Number(uploadClubId)) {
      window.alert('Als Plattform-Admin: Bitte den Zielverein für private Archiv-Uploads wählen (club_id).')
      return
    }
    // P-06: Rechte-Dialog vor Upload anzeigen
    setPendingUploadFiles(list)
    setRightsDialogOpen(true)
  }

  const doUploadWithDecl = async (decl) => {
    setRightsDialogOpen(false)
    const list = pendingUploadFiles
    setPendingUploadFiles(null)
    if (!list?.length) return
    setUploadBusy(true)
    setUploadSummary('')
    try {
      const res = await api.bulkUploadMediaAssets(list, {
        visibility: uploadVis,
        ...((uploadVis === 'club' || (uploadVis === 'private' && isPlatformAdmin)) && Number(uploadClubId)
          ? { club_id: Number(uploadClubId) }
          : {}),
        ...decl,
      })
      setUploadSummary(
        `Archiv-Upload: neu ${res.created_count}, bereits vorhanden ${res.duplicate_count}, fehlgeschlagen ${res.failed_count}. Liste aktualisiert.`,
      )
      if (res.created_count > 0 || res.duplicate_count > 0) {
        setFilterUploaderId('')
      }
      await loadItems()
      window.requestAnimationFrame(() => {
        gridTopAnchorRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })
      })
    } catch (err) {
      window.alert(err.message || String(err))
    } finally {
      setUploadBusy(false)
    }
  }

  return (
    <div className="app-page media-library">
      <RightsDeclarationDialog
        open={rightsDialogOpen}
        onCancel={() => { setRightsDialogOpen(false); setPendingUploadFiles(null) }}
        onConfirm={doUploadWithDecl}
        targetVisibility={uploadVis}
        mode="upload"
      />
      <RightsDeclarationDialog
        open={rightsUpgradeDialogOpen}
        onCancel={() => { setRightsUpgradeDialogOpen(false); setPendingPatchBody(null) }}
        onConfirm={doSaveWithRightsDecl}
        targetVisibility={pendingPatchBody?.visibility || modal?.visibility || 'club'}
        isPromotion={true}
        mode={rightsUpgradeMode}
      />
      <div className="media-library__container">
        <header className="media-library__hero">
          <div className="media-library__hero-row">
            <h1 className="media-library__title">Medienbibliothek</h1>
            <div className="media-library__hero-links">
              <Link to="/">Übersicht</Link>
              <Link to="/exercises">Übungen</Link>
              {isSuperadmin ? <Link to="/admin">Globale Administration</Link> : null}
              {hasClubOrgAdmin ? <Link to="/clubs">Vereine &amp; Mitglieder</Link> : null}
            </div>
          </div>
          <p className="media-library__intro">
            Offizielle und vereinsfreigegebene Medien sind für alle passenden Nutzer sichtbar. Eigene private Medien
            kannst du bearbeiten, veröffentlichen oder in den Papierkorb legen; im Papierkorb siehst du als Standardnutzer
            nur deine eigenen privaten Objekte, als Vereinsadmin zusätzlich den Vereins-Papierkorb. Vereins-Rollen können
            Vereins-Medien verwalten, aber nicht bis „Offiziell“ anheben — das bleibt dem Superadmin vorbehalten.
            Plattform-Admins geben beim privaten Upload den Zielverein an (club_id).
          </p>
        </header>

        <div className="media-library__toolbar">
          <div className="media-library__toolbar-row">
            <input
              type="search"
              className="form-input media-library__search"
              placeholder="Suche Bezeichner, Pfade, Copyright, Tags …"
              value={q}
              onChange={(e) => setQ(e.target.value)}
              aria-label="Suche"
            />
            <select
              className="form-input media-library__select"
              value={lifecycle}
              onChange={(e) => setLifecycle(e.target.value)}
              aria-label="Lebenszyklus"
            >
              {LC_OPTIONS.map((o) => (
                <option key={o.value} value={o.value}>
                  {o.label}
                </option>
              ))}
            </select>
            <div className="media-library__view-toggle" role="group" aria-label="Darstellung">
              <button
                type="button"
                className={`media-library__icon-btn${viewMode === 'grid' ? ' media-library__icon-btn--on' : ''}`}
                onClick={() => setViewMode('grid')}
                title="Kacheln"
              >
                <LayoutGrid size={20} />
              </button>
              <button
                type="button"
                className={`media-library__icon-btn${viewMode === 'list' ? ' media-library__icon-btn--on' : ''}`}
                onClick={() => setViewMode('list')}
                title="Liste"
              >
                <List size={20} />
              </button>
            </div>
            <button
              type="button"
              className="btn btn-secondary media-library__refresh"
              onClick={loadItems}
              disabled={loading}
            >
              Aktualisieren
            </button>
          </div>
          <div className="media-library__filters-row" aria-label="Filter">
            <select
              className="form-input"
              value={mediaKind}
              onChange={(e) => setMediaKind(e.target.value)}
              aria-label="Medientyp"
            >
              {MEDIA_KIND_OPTIONS.map((o) => (
                <option key={o.value} value={o.value}>
                  {o.label}
                </option>
              ))}
            </select>
            {isSuperadmin ? (
              <select
                className="form-input"
                value={filterClubId}
                onChange={(e) => setFilterClubId(e.target.value)}
                aria-label="Verein (Filter)"
              >
                <option value="">Alle Vereine</option>
                {clubs.map((c) => (
                  <option key={c.id} value={String(c.id)}>
                    {c.name || c.id}
                  </option>
                ))}
              </select>
            ) : null}
            {viewer?.show_uploader_meta ? (
              <select
                className="form-input"
                value={filterUploaderId}
                onChange={(e) => setFilterUploaderId(e.target.value)}
                aria-label="Uploader"
              >
                <option value="">Alle Uploader</option>
                {uploaderFilterOptions.map((u) => (
                  <option key={u.id} value={String(u.id)}>
                    {u.label}
                  </option>
                ))}
              </select>
            ) : null}
          </div>
          <div className="media-library__upload-row" aria-label="Archiv hochladen">
            <input
              ref={bulkFileInputRef}
              type="file"
              className="media-library__sr-file"
              accept="image/*,video/*,application/pdf"
              multiple
              onChange={onBulkArchiveFiles}
            />
            <button
              type="button"
              className="btn btn-secondary"
              disabled={uploadBusy}
              onClick={() => bulkFileInputRef.current?.click()}
              title="Mehrere Dateien ins Archiv laden"
            >
              <Upload size={18} aria-hidden className="media-library__upload-icon" />
              Archiv-Upload
            </button>
            <select
              className="form-input"
              value={uploadVis}
              onChange={(e) => {
                setUploadVis(e.target.value)
                setUploadSummary('')
              }}
              aria-label="Sichtbarkeit für neuen Upload"
            >
              {archiveVisOptions.map((o) => (
                <option key={o.value} value={o.value}>
                  Upload: {o.label}
                </option>
              ))}
            </select>
            {uploadVis === 'club' || (uploadVis === 'private' && isPlatformAdmin) ? (
              <select
                className="form-input"
                value={uploadClubId}
                onChange={(e) => setUploadClubId(e.target.value)}
                aria-label="Verein für Upload"
              >
                <option value="">Verein wählen…</option>
                {clubs.map((c) => (
                  <option key={c.id} value={String(c.id)}>
                    {c.name || c.id}
                  </option>
                ))}
              </select>
            ) : null}
            {uploadSummary ? (
              <span className="media-library__upload-summary" role="status">
                {uploadSummary}
              </span>
            ) : null}
          </div>
          <div className="media-library__toolbar-meta">
            <label className="media-library__check-all">
              <input type="checkbox" checked={items.length > 0 && selCount === items.length} onChange={selectAll} />
              <span>alle sichtbaren</span>
            </label>
            {selCount > 0 ? (
              <button type="button" className="btn btn-secondary" onClick={() => setBulkOpen(true)}>
                Bulk ({selCount})
              </button>
            ) : null}
          </div>
        </div>

        {error ? (
          <p className="media-library__err" role="alert">
            {error}
          </p>
        ) : null}

        <div ref={gridTopAnchorRef} className="media-library__grid-top-anchor" aria-hidden="true" />

        {loading && !items.length ? <div className="spinner media-library__spinner" /> : null}

        {!loading && !items.length && !error ? (
          <p className="media-library__empty">Keine Medien für diese Filter.</p>
        ) : null}

        {viewMode === 'grid' && items.length > 0 ? (
          <div className="media-library__grid">
            {items.map((it) => {
              const chk = selected.has(it.id)
              return (
                <div key={it.id} className="media-library__card">
                  <label className="media-library__card-check">
                    <input type="checkbox" checked={chk} onChange={() => toggleSel(it.id)} />
                  </label>
                  <button
                    type="button"
                    className="media-library__card-menu"
                    onClick={() => openEdit(it)}
                    title="Bearbeiten"
                    aria-label="Bearbeiten"
                  >
                    <MoreVertical size={20} />
                  </button>
                  <button
                    type="button"
                    className="media-library__card-thumb-hit"
                    onClick={() => setPreview(it)}
                    title="Vorschau"
                  >
                    <div className="media-library__card-thumb-wrap">
                      <MediaTypeGlyph mimeType={it.mime_type} />
                      <MediaThumb mediaId={it.id} mimeType={it.mime_type} />
                      {(it.copyright_notice || '').trim() ? (
                        <span
                          className="media-library__card-copyright"
                          title={(it.copyright_notice || '').trim()}
                          aria-label="Copyright-Eintrag vorhanden"
                        >
                          <Copyright size={14} strokeWidth={2} aria-hidden />
                        </span>
                      ) : null}
                    </div>
                  </button>
                  <div className="media-library__card-footer">
                    <div className="media-library__card-name" title={it.original_filename || `#${it.id}`}>
                      {it.original_filename || `Medium #${it.id}`}
                    </div>
                    <div className="media-library__card-footer-row">
                      <MediaCardScopeStatus visibility={it.visibility} lifecycleState={it.lifecycle_state} />
                      {it.legal_hold_active && (
                        <span className="media-library__legal-hold-badge" title="Sofortsperre aktiv (Legal Hold)">
                          Sofort gesperrt
                        </span>
                      )}
                      {!it.legal_hold_active && it.rights_status === 'legacy_unreviewed' && (
                        <span
                          style={{ fontSize: '0.7rem', color: 'var(--danger)', marginLeft: 4 }}
                          title="Altbestand – Rechtserklärung nach neuem Standard (P-06) noch nicht erfasst"
                        >Altbestand ⚠</span>
                      )}
                      {it.open_report_count > 0 && (
                        <span
                          style={{
                            fontSize: '0.7rem',
                            background: 'var(--danger)',
                            color: '#fff',
                            borderRadius: '10px',
                            padding: '1px 6px',
                            marginLeft: 4,
                            fontWeight: 600,
                          }}
                          title={`${it.open_report_count} offene Inhaltsmeldung${it.open_report_count > 1 ? 'en' : ''}`}
                        >{it.open_report_count} Meldung{it.open_report_count > 1 ? 'en' : ''}</span>
                      )}
                    </div>
                    {(it.tags || []).length ? (
                      <div className="media-library__tag-chips">
                        {(it.tags || []).slice(0, 6).map((tg) => (
                          <span key={tg} className="media-library__tag-chip">
                            {tg}
                          </span>
                        ))}
                      </div>
                    ) : null}
                    <MediaUsageBlock usage={it.usage} compact />
                    {(it.lifecycle_state || 'active') === 'active' && !it.legal_hold_active && (
                      <button
                        type="button"
                        onClick={() => setReportTarget(it)}
                        style={{
                          background: 'none',
                          border: 'none',
                          padding: '2px 0 0',
                          cursor: 'pointer',
                          fontSize: '0.72rem',
                          color: 'var(--text3)',
                          textDecoration: 'underline',
                          textAlign: 'left',
                        }}
                      >
                        Melden
                      </button>
                    )}
                  </div>
                </div>
              )
            })}
          </div>
        ) : null}

        {viewMode === 'list' && items.length > 0 ? (
          <div className="media-library__table-wrap">
            <table className="media-library__table">
              <thead>
                <tr>
                  <th className="media-library__th-check" />
                  <th>Vorschau</th>
                  <th>Bezeichnung</th>
                  <th>Kennzeichen</th>
                  <th className="media-library__th-tags">Tags</th>
                  <th className="media-library__th-usage">Verwendung</th>
                  {viewer?.show_club_meta ? <th>Verein</th> : null}
                  {viewer?.show_uploader_meta ? <th>Uploader</th> : null}
                  <th className="media-library__th-act" />
                </tr>
              </thead>
              <tbody>
                {items.map((it) => {
                  return (
                    <tr key={it.id}>
                      <td>
                        <input type="checkbox" checked={selected.has(it.id)} onChange={() => toggleSel(it.id)} />
                      </td>
                      <td className="media-library__td-thumb">
                        <button
                          type="button"
                          className="media-library__table-thumb-hit"
                          onClick={() => setPreview(it)}
                          title="Vorschau"
                        >
                          <div className="media-library__table-thumb">
                            <MediaTypeGlyph mimeType={it.mime_type} compact />
                            <MediaThumb mediaId={it.id} mimeType={it.mime_type} />
                          </div>
                        </button>
                      </td>
                      <td className="media-library__td-name">{it.original_filename || `#${it.id}`}</td>
                      <td className="media-library__td-glyphs">
                        <MediaCardScopeStatus visibility={it.visibility} lifecycleState={it.lifecycle_state} />
                      </td>
                      <td className="media-library__td-tags media-library__td-sub">
                        {(it.tags || []).length ? (it.tags || []).join(', ') : '—'}
                      </td>
                      <td className="media-library__td-usage media-library__td-sub">
                        <MediaUsageBlock usage={it.usage} compact />
                      </td>
                      {viewer?.show_club_meta ? (
                        <td className="media-library__td-sub">{it.club_name || it.club_id || '—'}</td>
                      ) : null}
                      {viewer?.show_uploader_meta ? (
                        <td className="media-library__td-sub">{uploaderLabel(it, viewer) || '—'}</td>
                      ) : null}
                      <td style={{ whiteSpace: 'nowrap' }}>
                        <button
                          type="button"
                          className="media-library__icon-btn"
                          onClick={() => openEdit(it)}
                          aria-label="Bearbeiten"
                        >
                          <MoreVertical size={18} />
                        </button>
                        {(it.lifecycle_state || 'active') === 'active' && !it.legal_hold_active && (
                          <button
                            type="button"
                            className="media-library__icon-btn"
                            onClick={() => setReportTarget(it)}
                            aria-label="Melden"
                            title="Inhalt melden"
                            style={{ color: 'var(--text3)', fontSize: '0.72rem', padding: '4px 6px' }}
                          >
                            Melden
                          </button>
                        )}
                      </td>
                    </tr>
                  )
                })}
              </tbody>
            </table>
          </div>
        ) : null}
      </div>

      {preview ? (
        <MediaPreviewModal
          title={preview.original_filename || `Medium #${preview.id}`}
          media={{ ...preview, asset_legal_hold_active: preview.legal_hold_active }}
          fileUrl={resolveMediaAssetFileUrl(preview.id)}
          onClose={() => setPreview(null)}
          onReport={
            (preview.lifecycle_state || 'active') === 'active' && !preview.legal_hold_active
              ? () => { setReportTarget(preview); setPreview(null) }
              : null
          }
          onEdit={() => { openEdit(preview); setPreview(null) }}
        />
      ) : null}

      {bulkOpen ? (
        <div className="media-library__overlay" role="dialog" aria-modal="true" aria-labelledby="bulk-title">
          <div className="media-library__modal media-library__modal--wide">
            <div className="media-library__modal-head">
              <h2 id="bulk-title">Bulk für {selCount} Medien</h2>
              <button type="button" className="media-library__icon-btn" onClick={() => setBulkOpen(false)} aria-label="Schließen">
                <X size={22} />
              </button>
            </div>
            <div className="media-library__modal-body">
              <p className="media-library__hint">Nur ausgefüllte Felder werden gesetzt. Fehlende Rechte pro Medium werden im Ergebnis gemeldet.</p>
              <label className="form-label">Copyright (optional)</label>
              <textarea
                className="form-input"
                rows={2}
                value={bulkCopyright}
                onChange={(e) => setBulkCopyright(e.target.value)}
              />
              <label className="media-library__check">
                <input
                  type="checkbox"
                  checked={bulkApplyVis}
                  onChange={(e) => setBulkApplyVis(e.target.checked)}
                />
                <span>Sichtbarkeit ändern</span>
              </label>
              {bulkApplyVis ? (
                <>
                  <select className="form-input" value={bulkVis} onChange={(e) => setBulkVis(e.target.value)}>
                    {archiveVisOptions.map((o) => (
                      <option key={o.value} value={o.value}>
                        {o.label}
                      </option>
                    ))}
                  </select>
                  {bulkVis === 'club' || (bulkVis === 'private' && isPlatformAdmin) ? (
                    <select className="form-input" value={bulkClubId} onChange={(e) => setBulkClubId(e.target.value)}>
                      <option value="">— Verein —</option>
                      {clubs.map((c) => (
                        <option key={c.id} value={String(c.id)}>
                          {c.name || c.id}
                        </option>
                      ))}
                    </select>
                  ) : null}
                </>
              ) : null}
              <div className="media-library__modal-actions">
                <button type="button" className="btn btn-secondary" disabled={busy} onClick={runBulkPatch}>
                  Metadaten anwenden
                </button>
                <button
                  type="button"
                  className="btn btn-secondary"
                  disabled={busy}
                  onClick={() => {
                    if (!window.confirm('Ausgewählte aktiven Medien in Papierkorb (1)?')) return
                    runBulkLifecycle('trash_soft')
                  }}
                >
                  Papierkorb (1)
                </button>
                {isSuperadmin ? (
                  <button
                    type="button"
                    className="btn btn-secondary"
                    disabled={busy}
                    onClick={() => {
                      if (!window.confirm('Superadmin: Ausgewählte endgültig löschen?')) return
                      runBulkLifecycle('superadmin_hard_delete')
                    }}
                  >
                    Hard-Delete
                  </button>
                ) : null}
              </div>
            </div>
          </div>
        </div>
      ) : null}

      {modal && modalDraft ? (
        <div className="media-library__overlay" role="dialog" aria-modal="true" aria-labelledby="edit-media-title">
          <div className="media-library__modal">
            <div className="media-library__modal-head">
              <h2 id="edit-media-title">
                Medium #{modal.id}
                {(modal.visibility || '').toLowerCase() === 'official' && !isSuperadmin
                  ? ' · Nur Lesen'
                  : ''}
              </h2>
              <button type="button" className="media-library__icon-btn" onClick={closeModal} aria-label="Schließen">
                <X size={22} />
              </button>
            </div>
            <div className="media-library__modal-body">
              {(viewer?.show_club_meta ||
                viewer?.show_uploader_meta ||
                (modal.visibility || '').toLowerCase() === 'official') && (
                <div className="media-library__meta-block">
                  {viewer?.show_club_meta ? (
                    <div>
                      <span className="media-library__meta-k">Verein</span>
                      <span className="media-library__meta-v">{modal.club_name || modal.club_id || '—'}</span>
                    </div>
                  ) : null}
                  {viewer?.show_uploader_meta ? (
                    <div>
                      <span className="media-library__meta-k">Uploader</span>
                      <span className="media-library__meta-v">{uploaderLabel(modal, viewer) || '—'}</span>
                    </div>
                  ) : null}
                  <div>
                    <span className="media-library__meta-k">Technisch</span>
                    <span className="media-library__meta-v mono">
                      {modal.mime_type || '—'} · ID {modal.id}
                      {isSuperadmin && modal.storage_key ? ` · ${modal.storage_key}` : ''}
                    </span>
                  </div>
                </div>
              )}

              {modal.permissions?.edit_metadata ? (
                <>
                  <label className="form-label">Bezeichnung (Dateiname / Anzeige)</label>
                  <input
                    className="form-input"
                    value={modalDraft.display_name}
                    onChange={(e) => setModalDraft((d) => ({ ...d, display_name: e.target.value }))}
                  />
                  <label className="form-label">Copyright</label>
                  <textarea
                    className="form-input"
                    rows={3}
                    value={modalDraft.copyright_notice}
                    onChange={(e) => setModalDraft((d) => ({ ...d, copyright_notice: e.target.value }))}
                  />
                  <label className="form-label">Schlagwörter (kommagetrennt)</label>
                  <input
                    className="form-input"
                    value={modalDraft.tags_input}
                    onChange={(e) => setModalDraft((d) => ({ ...d, tags_input: e.target.value }))}
                    placeholder="z. B. Technik, Wurf"
                  />
                </>
              ) : (modal.visibility || '').toLowerCase() === 'official' ? (
                <>
                  <p className="media-library__hint">
                    Offizielle Medien sind für alle sichtbar. Bearbeiten, Sichtbarkeit und Löschung nur als Superadmin.
                  </p>
                  <label className="form-label">Bezeichnung</label>
                  <input className="form-input" readOnly value={modalDraft.display_name} />
                  <label className="form-label">Copyright</label>
                  <textarea className="form-input" readOnly rows={3} value={modalDraft.copyright_notice} />
                  <label className="form-label">Schlagwörter</label>
                  <input className="form-input" readOnly value={modalDraft.tags_input} />
                </>
              ) : (
                <p className="media-library__hint">Keine Berechtigung für Metadaten — nur Verwaltende dieser Stufe.</p>
              )}

              {modal.permissions?.change_visibility ? (
                <>
                  <label className="form-label">Sichtbarkeit</label>
                  <select
                    className="form-input"
                    value={modalDraft.visibility}
                    onChange={(e) => setModalDraft((d) => ({ ...d, visibility: e.target.value }))}
                  >
                    {modalVisibilityOptions.map((o) => (
                      <option key={o.value} value={o.value}>
                        {o.label}
                      </option>
                    ))}
                  </select>
                  {modalDraft.visibility === 'club' || (modalDraft.visibility === 'private' && isPlatformAdmin) ? (
                    <>
                      <label className="form-label">Verein</label>
                      <select
                        className="form-input"
                        value={modalDraft.club_id}
                        onChange={(e) => setModalDraft((d) => ({ ...d, club_id: e.target.value }))}
                      >
                        <option value="">— wählen —</option>
                        {clubs.map((c) => (
                          <option key={c.id} value={String(c.id)}>
                            {c.name || c.id}
                          </option>
                        ))}
                      </select>
                    </>
                  ) : null}
                </>
              ) : null}

              <div className="media-library__meta-block">
                <span className="media-library__meta-k">Sichtbarkeit / Lebenszyklus</span>
                <div className="media-library__meta-v" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <MediaCardScopeStatus visibility={modal.visibility} lifecycleState={modal.lifecycle_state} />
                </div>
              </div>

              <div className="media-library__meta-block">
                <span className="media-library__meta-k">Verwendung</span>
                <div className="media-library__meta-v">
                  <MediaUsageBlock usage={modal.usage} compact={false} />
                </div>
              </div>

              <div className="media-library__modal-actions">
                {modal.permissions?.edit_metadata ? (
                  <button type="button" className="btn btn-secondary" disabled={busy} onClick={saveModal}>
                    Speichern
                  </button>
                ) : null}
              </div>

              <div className="media-library__lc-block">
                <div className="media-library__lc-title">Lebenszyklus</div>
                <div className="media-library__lc-btns">
                  {modal.permissions?.trash_soft ? (
                    <button
                      type="button"
                      className="btn btn-secondary"
                      disabled={busy}
                      onClick={() =>
                        runLc(modal.id, 'trash_soft', 'In Papierkorb (Stufe 1) legen?')
                      }
                    >
                      Papierkorb
                    </button>
                  ) : null}
                  {modal.permissions?.trash_hidden ? (
                    <button
                      type="button"
                      className="btn btn-secondary"
                      disabled={busy}
                      onClick={() =>
                        runLc(
                          modal.id,
                          'trash_hidden',
                          'Ausblenden (Stufe 2)? Öffentliche Ansicht verliert das Medium.',
                        )
                      }
                    >
                      Ausblenden
                    </button>
                  ) : null}
                  {modal.permissions?.recover ? (
                    <button type="button" className="btn btn-secondary" disabled={busy} onClick={() => runLc(modal.id, 'recover', null)}>
                      ↩ Stufe 1
                    </button>
                  ) : null}
                  {modal.permissions?.reactivate ? (
                    <button
                      type="button"
                      className="btn btn-secondary"
                      disabled={busy}
                      onClick={() => runLc(modal.id, 'reactivate', null)}
                    >
                      Wieder aktiv
                    </button>
                  ) : null}
                  {modal.permissions?.purge ? (
                    <button
                      type="button"
                      className="btn btn-secondary"
                      disabled={busy}
                      onClick={() =>
                        runLc(
                          modal.id,
                          'purge',
                          'Endgültig löschen (nur Superadmin, Stufe 2)? Datei und DB-Eintrag.',
                        )
                      }
                    >
                      Endgültig löschen
                    </button>
                  ) : null}
                </div>
              </div>

              {isSuperadmin ? (
                <div className="media-library__lc-block">
                  <div className="media-library__lc-title">Medienjournal</div>
                  <button
                    type="button"
                    className="btn btn-secondary"
                    disabled={journalLoading}
                    onClick={() => openJournal(modal)}
                    style={{ display: 'flex', alignItems: 'center', gap: 6 }}
                  >
                    <ScrollText size={14} aria-hidden />
                    Einwilligungs-Journal
                  </button>
                </div>
              ) : null}

              {isSuperadmin ? (
                <div className="media-library__lc-block media-library__lc-block--legal-hold">
                  <div className="media-library__lc-title">Sofortsperre (Legal Hold)</div>
                  {modal.legal_hold_active ? (
                    <div>
                      <p className="media-library__legal-hold-info">
                        <strong>Aktiv seit:</strong>{' '}
                        {modal.legal_hold_set_at ? new Date(modal.legal_hold_set_at).toLocaleString('de-DE') : '—'}
                        {modal.legal_hold_reason_code ? (
                          <> · <strong>Grund:</strong> {LEGAL_HOLD_REASON_LABELS[modal.legal_hold_reason_code] || modal.legal_hold_reason_code}</>
                        ) : null}
                      </p>
                      {modal.legal_hold_reason_note ? (
                        <p className="media-library__legal-hold-info">{modal.legal_hold_reason_note}</p>
                      ) : null}
                      <button
                        type="button"
                        className="btn btn-secondary"
                        disabled={busy}
                        onClick={() => openLegalHoldRelease(modal)}
                      >
                        Sperre aufheben
                      </button>
                    </div>
                  ) : (
                    <button
                      type="button"
                      className="btn btn-secondary media-library__btn--legal-hold"
                      disabled={busy}
                      onClick={() => openLegalHoldSet(modal)}
                    >
                      Sofort sperren
                    </button>
                  )}
                </div>
              ) : null}

              {modal.permissions?.superadmin_lifecycle ? (
                <div className="media-library__lc-block media-library__lc-block--danger">
                  <div className="media-library__lc-title">Superadmin</div>
                  <div className="media-library__row">
                    <select
                      className="form-input"
                      value={modalDraft.superTarget}
                      onChange={(e) => setModalDraft((d) => ({ ...d, superTarget: e.target.value }))}
                    >
                      <option value="active">aktiv</option>
                      <option value="trash_soft">Papierkorb 1</option>
                      <option value="trash_hidden">ausgeblendet 2</option>
                    </select>
                    <button
                      type="button"
                      className="btn btn-secondary"
                      disabled={busy}
                      onClick={() =>
                        runLc(
                          modal.id,
                          'superadmin_force_lifecycle',
                          `Zustand erzwingen: ${modalDraft.superTarget}?`,
                          { target_lifecycle: modalDraft.superTarget },
                        )
                      }
                    >
                      Zustand setzen
                    </button>
                  </div>
                  {modal.permissions?.superadmin_hard_delete ? (
                    <button
                      type="button"
                      className="btn btn-secondary"
                      disabled={busy}
                      onClick={() =>
                        runLc(
                          modal.id,
                          'superadmin_hard_delete',
                          'Medium komplett entfernen (DB + Datei)?',
                        )
                      }
                    >
                      Komplett löschen
                    </button>
                  ) : null}
                </div>
              ) : null}
            </div>
          </div>
        </div>
      ) : null}

      {journalModal ? (
        <div className="media-library__overlay" role="dialog" aria-modal="true" aria-labelledby="journal-modal-title">
          <div className="media-library__modal media-library__modal--wide">
            <div className="media-library__modal-head">
              <h2 id="journal-modal-title">
                Journal · Medium #{journalModal.asset.id}
                {journalModal.asset.original_filename ? ` · ${journalModal.asset.original_filename}` : ''}
              </h2>
              <button
                type="button"
                className="media-library__icon-btn"
                onClick={() => { setJournalModal(null); setJournalCorrectionOpen(false) }}
                aria-label="Schließen"
              >
                <X size={22} />
              </button>
            </div>
            <div className="media-library__modal-body">
              <div className="media-library__meta-block">
                <div>
                  <span className="media-library__meta-k">Rechtsstatus</span>
                  <span className="media-library__meta-v">{journalModal.asset.rights_status}</span>
                </div>
                <div>
                  <span className="media-library__meta-k">Sichtbarkeit</span>
                  <span className="media-library__meta-v">{visibilityUiLabel(journalModal.asset.visibility)}</span>
                </div>
                {journalModal.asset.copyright_notice ? (
                  <div>
                    <span className="media-library__meta-k">Copyright</span>
                    <span className="media-library__meta-v">{journalModal.asset.copyright_notice}</span>
                  </div>
                ) : null}
              </div>

              {/* Chronologische Event-Timeline */}
              {(journalModal.events || journalModal.declarations || []).length === 0 ? (
                <p className="media-library__hint">Noch keine Einträge für dieses Medium.</p>
              ) : (
                <div className="media-library__journal-list">
                  {(journalModal.events || journalModal.declarations || []).map((evt, idx) => {
                    if (evt.kind === 'audit') {
                      const byLabel = evt.acting_name || evt.acting_email
                        || (evt.acting_profile_id ? `Profil #${evt.acting_profile_id}` : '—')
                      const old = evt.old_values || {}
                      const nw = evt.new_values || {}
                      return (
                        <div key={`a-${idx}`} className="media-library__journal-entry media-library__journal-entry--audit">
                          <div className="media-library__journal-entry-head">
                            <span className="media-library__journal-action media-library__journal-action--audit">
                              {eventTypeLabel(evt.event_type)}
                            </span>
                            <span className="media-library__journal-date">
                              {new Date(evt.occurred_at).toLocaleString('de-DE')}
                            </span>
                          </div>
                          <div className="media-library__journal-entry-by">{byLabel}</div>
                          <div className="media-library__journal-audit-vals">
                            {evt.event_type === 'visibility_change' ? (
                              <span>{visLabel(old.visibility)} → {visLabel(nw.visibility)}</span>
                            ) : evt.event_type === 'lifecycle_change' ? (
                              <span>{lcLabel(old.lifecycle_state)} → {lcLabel(nw.lifecycle_state)}</span>
                            ) : evt.event_type === 'copyright_change' ? (
                              <>
                                <div className="media-library__journal-audit-row">
                                  <span className="media-library__journal-audit-label">Alt:</span>
                                  <span>{old.copyright_notice || '—'}</span>
                                </div>
                                <div className="media-library__journal-audit-row">
                                  <span className="media-library__journal-audit-label">Neu:</span>
                                  <span>{nw.copyright_notice || '—'}</span>
                                </div>
                              </>
                            ) : evt.event_type === 'legal_hold_set' ? (
                              <>
                                <div className="media-library__journal-audit-row">
                                  <span className="media-library__journal-audit-label">Grund:</span>
                                  <span>{LEGAL_HOLD_REASON_LABELS[nw.reason_code] || nw.reason_code || '—'}</span>
                                </div>
                                {nw.reason_note ? (
                                  <div className="media-library__journal-audit-row">
                                    <span className="media-library__journal-audit-label">Begründung:</span>
                                    <span>{nw.reason_note}</span>
                                  </div>
                                ) : null}
                              </>
                            ) : evt.event_type === 'legal_hold_released' ? (
                              <>
                                {nw.release_note ? (
                                  <div className="media-library__journal-audit-row">
                                    <span className="media-library__journal-audit-label">Freigabegrund:</span>
                                    <span>{nw.release_note}</span>
                                  </div>
                                ) : null}
                                <div className="media-library__journal-audit-row">
                                  <span className="media-library__journal-audit-label">Rechtsstatus wiederhergestellt:</span>
                                  <span>{nw.rights_status || '—'}</span>
                                </div>
                              </>
                            ) : evt.event_type === 'content_report_filed' ? (
                              <>
                                {nw.content_report_id ? (
                                  <div className="media-library__journal-audit-row">
                                    <span className="media-library__journal-audit-label">Meldungs-ID:</span>
                                    <span>#{nw.content_report_id}</span>
                                  </div>
                                ) : null}
                                {nw.report_reason ? (
                                  <div className="media-library__journal-audit-row">
                                    <span className="media-library__journal-audit-label">Meldegrund:</span>
                                    <span>{nw.report_reason}</span>
                                  </div>
                                ) : null}
                                {nw.priority ? (
                                  <div className="media-library__journal-audit-row">
                                    <span className="media-library__journal-audit-label">Priorität:</span>
                                    <span>{nw.priority}</span>
                                  </div>
                                ) : null}
                                {nw.status ? (
                                  <div className="media-library__journal-audit-row">
                                    <span className="media-library__journal-audit-label">Status:</span>
                                    <span>{old.status ? `${old.status} → ` : ''}{nw.status}</span>
                                  </div>
                                ) : null}
                                {nw.begründung ? (
                                  <div className="media-library__journal-audit-row">
                                    <span className="media-library__journal-audit-label">Begründung:</span>
                                    <span>{nw.begründung}</span>
                                  </div>
                                ) : null}
                              </>
                            ) : (
                              Object.keys(nw).map((k) => (
                                <div key={k} className="media-library__journal-audit-row">
                                  <span className="media-library__journal-audit-label">{k}:</span>
                                  <span>{String(old[k] ?? '—')} → {String(nw[k] ?? '—')}</span>
                                </div>
                              ))
                            )}
                          </div>
                        </div>
                      )
                    }
                    // kind === 'declaration' (or legacy array format)
                    const d = evt
                    const byLabel = d.declared_by_name || d.declared_by_email
                      || (d.declared_by_profile_id ? `Profil #${d.declared_by_profile_id}` : '—')
                    return (
                      <div key={`d-${idx}`} className={`media-library__journal-entry${d.action_type === 'correction' ? ' media-library__journal-entry--correction' : ''}`}>
                        <div className="media-library__journal-entry-head">
                          <span className={`media-library__journal-action${d.action_type === 'correction' ? ' media-library__journal-action--correction' : ''}`}>
                            {actionTypeLabel(d.action_type)}
                          </span>
                          <span className="media-library__journal-arrow">→</span>
                          <span className="media-library__journal-vis">{visibilityUiLabel(d.target_visibility)}</span>
                          <span className="media-library__journal-date">
                            {new Date(d.declared_at).toLocaleString('de-DE')}
                          </span>
                        </div>
                        <div className="media-library__journal-entry-by">
                          {byLabel}
                          <span className="media-library__journal-version"> · {d.declaration_version}</span>
                        </div>
                        {d.correction_note ? (
                          <div className="media-library__journal-correction-note">
                            <strong>Korrekturgrund:</strong> {d.correction_note}
                          </div>
                        ) : null}
                        <div className="media-library__journal-fields">
                          <JournalBoolField label="Rechteinhaber bestätigt" val={d.rights_holder_confirmed} />
                          <JournalBoolField label="Erkennbare Personen" val={d.contains_identifiable_persons} />
                          {d.contains_identifiable_persons ? (
                            <JournalBoolField
                              label="Einwilligung Personen"
                              val={d.person_consent_confirmed}
                              context={d.person_consent_context}
                            />
                          ) : null}
                          <JournalBoolField label="Minderjährige" val={d.contains_minors} />
                          {d.contains_minors ? (
                            <JournalBoolField
                              label="Erziehungsberechtigte Einwilligung"
                              val={d.parental_consent_confirmed}
                              context={d.parental_consent_context}
                            />
                          ) : null}
                          <JournalBoolField label="Musik" val={d.contains_music} />
                          {d.contains_music ? (
                            <JournalBoolField
                              label="Musikrechte"
                              val={d.music_rights_confirmed}
                              context={d.music_rights_context}
                            />
                          ) : null}
                          <JournalBoolField label="Drittinhalte" val={d.contains_third_party_content} />
                          {d.contains_third_party_content ? (
                            <JournalBoolField
                              label="Drittrechte bestätigt"
                              val={d.third_party_rights_confirmed}
                              context={d.third_party_rights_context}
                            />
                          ) : null}
                        </div>
                      </div>
                    )
                  })}
                </div>
              )}

              {/* Korrektur-Formular */}
              {journalModal.can_correct && !journalCorrectionOpen ? (
                <div style={{ marginTop: 16 }}>
                  <button
                    className="btn btn-secondary"
                    onClick={() => {
                      setJournalCorrectionDraft({
                        target_visibility: journalModal.asset.visibility || 'private',
                        rights_holder_confirmed: false,
                        contains_identifiable_persons: false,
                        person_consent_confirmed: null,
                        contains_minors: false,
                        parental_consent_confirmed: null,
                        contains_music: false,
                        music_rights_confirmed: null,
                        contains_third_party_content: false,
                        third_party_rights_confirmed: null,
                        correction_note: '',
                      })
                      setJournalCorrectionOpen(true)
                    }}
                  >
                    Korrektur erfassen
                  </button>
                </div>
              ) : null}

              {journalCorrectionOpen && journalCorrectionDraft ? (
                <div className="media-library__journal-correction-form">
                  <h3 className="media-library__journal-correction-title">Korrektur der Einwilligungserklärung</h3>
                  <p className="media-library__hint" style={{ marginBottom: 12 }}>
                    Die Korrektur wird als neuer Eintrag im Journal gespeichert (append-only). Die ursprünglichen Einträge bleiben erhalten.
                  </p>
                  <div className="form-row">
                    <label className="form-label">Ziel-Sichtbarkeit</label>
                    <select
                      className="form-input"
                      value={journalCorrectionDraft.target_visibility}
                      onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, target_visibility: e.target.value }))}
                    >
                      <option value="private">Privat</option>
                      <option value="club">Verein</option>
                      <option value="official">Offiziell</option>
                    </select>
                  </div>
                  <div className="form-row">
                    <label className="form-label">
                      <input
                        type="checkbox"
                        checked={!!journalCorrectionDraft.rights_holder_confirmed}
                        onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, rights_holder_confirmed: e.target.checked }))}
                        style={{ marginRight: 8 }}
                      />
                      Ich bestätige, dass ich die erforderlichen Rechte an diesem Medium besitze *
                    </label>
                  </div>
                  <div className="form-row">
                    <label className="form-label">
                      <input
                        type="checkbox"
                        checked={!!journalCorrectionDraft.contains_identifiable_persons}
                        onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, contains_identifiable_persons: e.target.checked, person_consent_confirmed: e.target.checked ? d.person_consent_confirmed : null }))}
                        style={{ marginRight: 8 }}
                      />
                      Erkennbare Personen abgebildet *
                    </label>
                  </div>
                  {journalCorrectionDraft.contains_identifiable_persons ? (
                    <div className="form-row" style={{ paddingLeft: 24 }}>
                      <label className="form-label">
                        <input
                          type="checkbox"
                          checked={!!journalCorrectionDraft.person_consent_confirmed}
                          onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, person_consent_confirmed: e.target.checked }))}
                          style={{ marginRight: 8 }}
                        />
                        Einwilligung aller erkennbaren Personen liegt vor *
                      </label>
                    </div>
                  ) : null}
                  <div className="form-row">
                    <label className="form-label">
                      <input
                        type="checkbox"
                        checked={!!journalCorrectionDraft.contains_minors}
                        onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, contains_minors: e.target.checked, parental_consent_confirmed: e.target.checked ? d.parental_consent_confirmed : null }))}
                        style={{ marginRight: 8 }}
                      />
                      Minderjährige abgebildet *
                    </label>
                  </div>
                  {journalCorrectionDraft.contains_minors ? (
                    <div className="form-row" style={{ paddingLeft: 24 }}>
                      <label className="form-label">
                        <input
                          type="checkbox"
                          checked={!!journalCorrectionDraft.parental_consent_confirmed}
                          onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, parental_consent_confirmed: e.target.checked }))}
                          style={{ marginRight: 8 }}
                        />
                        Einwilligung der Erziehungsberechtigten liegt vor *
                      </label>
                    </div>
                  ) : null}
                  <div className="form-row">
                    <label className="form-label">
                      <input
                        type="checkbox"
                        checked={!!journalCorrectionDraft.contains_music}
                        onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, contains_music: e.target.checked, music_rights_confirmed: e.target.checked ? d.music_rights_confirmed : null }))}
                        style={{ marginRight: 8 }}
                      />
                      Musik enthalten *
                    </label>
                  </div>
                  {journalCorrectionDraft.contains_music ? (
                    <div className="form-row" style={{ paddingLeft: 24 }}>
                      <label className="form-label">
                        <input
                          type="checkbox"
                          checked={!!journalCorrectionDraft.music_rights_confirmed}
                          onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, music_rights_confirmed: e.target.checked }))}
                          style={{ marginRight: 8 }}
                        />
                        Musikrechte liegen vor *
                      </label>
                    </div>
                  ) : null}
                  <div className="form-row">
                    <label className="form-label">
                      <input
                        type="checkbox"
                        checked={!!journalCorrectionDraft.contains_third_party_content}
                        onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, contains_third_party_content: e.target.checked, third_party_rights_confirmed: e.target.checked ? d.third_party_rights_confirmed : null }))}
                        style={{ marginRight: 8 }}
                      />
                      Fremdinhalte (Logos, Grafiken etc.) enthalten *
                    </label>
                  </div>
                  {journalCorrectionDraft.contains_third_party_content ? (
                    <div className="form-row" style={{ paddingLeft: 24 }}>
                      <label className="form-label">
                        <input
                          type="checkbox"
                          checked={!!journalCorrectionDraft.third_party_rights_confirmed}
                          onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, third_party_rights_confirmed: e.target.checked }))}
                          style={{ marginRight: 8 }}
                        />
                        Rechte an Fremdinhalten liegen vor *
                      </label>
                    </div>
                  ) : null}
                  <div className="form-row">
                    <label className="form-label">Korrekturgrund (empfohlen)</label>
                    <textarea
                      className="form-input"
                      rows={2}
                      value={journalCorrectionDraft.correction_note || ''}
                      onChange={(e) => setJournalCorrectionDraft((d) => ({ ...d, correction_note: e.target.value }))}
                      placeholder="Warum wird die Erklärung korrigiert?"
                    />
                  </div>
                  <div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
                    <button
                      className="btn btn-primary"
                      disabled={journalCorrectionBusy}
                      onClick={async () => {
                        setJournalCorrectionBusy(true)
                        try {
                          await api.addMediaAssetDeclarationCorrection(journalModal.asset.id, journalCorrectionDraft)
                          setJournalCorrectionOpen(false)
                          const fresh = await api.getMediaAssetJournal(journalModal.asset.id)
                          setJournalModal(fresh)
                        } catch (e) {
                          alert(e.message || String(e))
                        } finally {
                          setJournalCorrectionBusy(false)
                        }
                      }}
                    >
                      {journalCorrectionBusy ? 'Speichern…' : 'Korrektur speichern'}
                    </button>
                    <button
                      className="btn btn-secondary"
                      disabled={journalCorrectionBusy}
                      onClick={() => setJournalCorrectionOpen(false)}
                    >
                      Abbrechen
                    </button>
                  </div>
                </div>
              ) : null}
            </div>
          </div>
        </div>
      ) : null}

      {legalHoldDialog ? (
        <div className="media-library__overlay" role="dialog" aria-modal="true" aria-labelledby="legal-hold-dialog-title">
          <div className="media-library__modal media-library__modal--narrow">
            <div className="media-library__modal-head">
              <h2 id="legal-hold-dialog-title">
                {legalHoldDialog.mode === 'set' ? 'Sofortsperre setzen' : 'Sofortsperre aufheben'}
              </h2>
              <button
                type="button"
                className="media-library__icon-btn"
                onClick={() => setLegalHoldDialog(null)}
                aria-label="Schließen"
                disabled={legalHoldBusy}
              >
                <X size={22} />
              </button>
            </div>
            <div className="media-library__modal-body">
              <p className="media-library__legal-hold-dialog-asset">
                Medium: <strong>{legalHoldDialog.assetName}</strong>
              </p>

              {legalHoldDialog.mode === 'set' ? (
                <>
                  <p className="media-library__hint media-library__legal-hold-warning">
                    Die Sofortsperre sperrt das Medium sofort für alle normalen Nutzer. Der Rechtsstatus wird auf „blocked" gesetzt. Die Sperre kann nur von Superadmins aufgehoben werden.
                  </p>
                  <div className="form-row">
                    <label className="form-label">Grund *</label>
                    <select
                      className="form-input"
                      value={legalHoldReasonCode}
                      onChange={(e) => setLegalHoldReasonCode(e.target.value)}
                      disabled={legalHoldBusy}
                    >
                      {LEGAL_HOLD_REASON_CODES.map((code) => (
                        <option key={code} value={code}>{LEGAL_HOLD_REASON_LABELS[code]}</option>
                      ))}
                    </select>
                  </div>
                  <div className="form-row">
                    <label className="form-label">Begründung * (mind. 5 Zeichen)</label>
                    <textarea
                      className="form-input"
                      rows={3}
                      value={legalHoldReasonNote}
                      onChange={(e) => setLegalHoldReasonNote(e.target.value)}
                      placeholder="Konkrete Begründung für die Sofortsperre..."
                      disabled={legalHoldBusy}
                    />
                  </div>
                </>
              ) : (
                <>
                  <p className="media-library__hint">
                    Die Aufhebung der Sofortsperre gibt das Medium wieder frei. Der Rechtsstatus wird wiederhergestellt.
                  </p>
                  <div className="form-row">
                    <label className="form-label">Freigabe-Begründung * (mind. 5 Zeichen)</label>
                    <textarea
                      className="form-input"
                      rows={3}
                      value={legalHoldReleaseNote}
                      onChange={(e) => setLegalHoldReleaseNote(e.target.value)}
                      placeholder="Grund für die Aufhebung der Sofortsperre..."
                      disabled={legalHoldBusy}
                    />
                  </div>
                </>
              )}

              <div style={{ display: 'flex', gap: 8, marginTop: 16 }}>
                <button
                  type="button"
                  className={`btn ${legalHoldDialog.mode === 'set' ? 'media-library__btn--legal-hold-confirm' : 'btn-primary'}`}
                  disabled={legalHoldBusy}
                  onClick={submitLegalHold}
                >
                  {legalHoldBusy
                    ? (legalHoldDialog.mode === 'set' ? 'Sperren…' : 'Aufheben…')
                    : (legalHoldDialog.mode === 'set' ? 'Jetzt sperren' : 'Sperre aufheben')}
                </button>
                <button
                  type="button"
                  className="btn btn-secondary"
                  disabled={legalHoldBusy}
                  onClick={() => setLegalHoldDialog(null)}
                >
                  Abbrechen
                </button>
              </div>
            </div>
          </div>
        </div>
      ) : null}

      {reportTarget && (
        <ReportContentModal
          targetType="media_asset"
          targetId={reportTarget.id}
          targetLabel={reportTarget.original_filename || `Medium #${reportTarget.id}`}
          onClose={() => setReportTarget(null)}
          onSuccess={() => {
            const id = reportTarget.id
            setItems((prev) => prev.map((it) =>
              it.id === id ? { ...it, open_report_count: (it.open_report_count || 0) + 1 } : it
            ))
          }}
        />
      )}
    </div>
  )
}
