// Review queue — pending submissions, oldest first.
//
// The heart of the admin app. Submissions enter pending, run through
// the AudD copyright check, become voteable when clean, auto-flip to
// accepted at 6+ votes × ≥90% accept ratio, OR an admin force-accepts.
//
// Audio playback flows through the global AudioService → NowPlayingBar
// at the bottom of the viewport. Per-card mini-player is gone; one
// click on a card's play button starts the bar at the bottom and
// every card stays in sync with `audio.currentId`.
//
// Filter chips above the list narrow by copyright status — useful
// when a backlog of pending checks builds up and you want to ignore
// them while reviewing the clean ones.

// How many cards to render on first paint. Real-world pending queues
// shouldn't normally hit this — tracks get accepted/rejected within
// hours — but keeping a safety net here means a temporarily-backed-up
// queue doesn't grind the page to a halt.
const PAGE_SIZE = 30;

function ReviewQueue({ submissions, me, refresh }) {
  const audio = useAudioStatus();
  const [query, setQuery] = React.useState('');
  const [filter, setFilter] = React.useState('all');   // all | clean | flagged | pending-cr
  const [expandedId, setExpandedId] = React.useState(null);
  const [visibleCount, setVisibleCount] = React.useState(PAGE_SIZE);

  // Search input gets the useDeferredValue treatment — keystrokes
  // stay smooth even if the queue is large because React schedules
  // the filter recomputation as a low-priority update.
  const deferredQuery = React.useDeferredValue(query);

  // Stop the global player if the track being acted on is the one
  // currently auditioning. Without this an accept/reject removes the
  // card from the queue but the audio keeps playing in the
  // NowPlayingBar with no obvious "what is this" context.
  const stopIfActive = (id) => {
    if (audio.currentId === id) AudioService.stop();
  };

  const onPlay = (sub) => {
    if (!sub?.audioUrl) return;
    audio.play(sub);
  };

  const onVote = async (id, vote) => {
    try {
      const updated = await Api.vote(id, vote);
      // Auto-accept might have flipped the status; if the card is
      // about to leave the queue, kill the audio.
      if (updated && updated.status && updated.status !== 'pending') {
        stopIfActive(id);
      }
      await refresh();
      toast.success(vote === 'accept' ? 'Stimme abgegeben: dafür' : 'Stimme abgegeben: dagegen');
    } catch (e) {
      toast.error('Abstimmen fehlgeschlagen: ' + e.message);
    }
  };

  const onForceAccept = async (id) => {
    if (!confirm('Diesen Track direkt veröffentlichen?')) return;
    try {
      stopIfActive(id);
      await Api.forceAccept(id);
      await refresh();
      toast.success('Track veröffentlicht.');
    } catch (e) {
      toast.error('Veröffentlichen fehlgeschlagen: ' + e.message);
    }
  };

  const onForceReject = async (id) => {
    if (!confirm('Diesen Track ablehnen? Die hochgeladenen Dateien werden vom CDN gelöscht.')) return;
    try {
      stopIfActive(id);
      await Api.forceReject(id);
      await refresh();
      toast.info('Track abgelehnt und vom CDN entfernt.');
    } catch (e) {
      toast.error('Ablehnen fehlgeschlagen: ' + e.message);
    }
  };

  const onCopyrightSkip = async (id) => {
    if (!confirm(
      'AudD-Copyright-Check für diesen Track überspringen?\n\n' +
      'Der Track wird sofort fürs Voting freigegeben (oder zurück in die Queue gestellt, ' +
      'falls er bereits vom Worker als kommerziell geflaggt war).\n\n' +
      'Damit übernimmst du als Admin die rechtliche Verantwortung.'
    )) return;
    try {
      await Api.copyrightSkip(id);
      await refresh();
      toast.success('Copyright-Check übersprungen.');
    } catch (e) {
      toast.error('Skip fehlgeschlagen: ' + e.message);
    }
  };

  const onBlockUser = async (id) => {
    const reason = prompt(
      'Creator dauerhaft sperren?\n\n' +
      'Der Track wird abgelehnt + vom CDN gelöscht, und der Spieler kann nichts mehr hochladen, ' +
      'bis du ihn unter „Sperren" wieder freigibst.\n\n' +
      'Optionaler Grund:'
    );
    if (reason === null) return;
    try {
      stopIfActive(id);
      await Api.blockUser(id, reason);
      await refresh();
      toast.info('Creator gesperrt und Track abgelehnt.');
    } catch (e) {
      toast.error('Sperren fehlgeschlagen: ' + e.message);
    }
  };

  // Slice the submissions down to the current view. Filter chips
  // come first so the counts they show reflect the underlying status
  // distribution; the search box narrows the resulting list.
  const pendingAll = submissions.filter(s => s.status === 'pending');
  const counts = {
    all:        pendingAll.length,
    clean:      pendingAll.filter(s => s.copyrightStatus === 'clean').length,
    flagged:    pendingAll.filter(s => s.copyrightStatus === 'flagged').length,
    pendingCr:  pendingAll.filter(s => s.copyrightStatus === 'pending').length,
  };

  const filtered = pendingAll
    .filter(s => {
      if (filter === 'clean')      return s.copyrightStatus === 'clean';
      if (filter === 'flagged')    return s.copyrightStatus === 'flagged';
      if (filter === 'pending-cr') return s.copyrightStatus === 'pending';
      return true;
    })
    .filter(s => {
      const q = deferredQuery.trim().toLowerCase();
      if (!q) return true;
      return s.title.toLowerCase().includes(q) ||
             (s.artist || '').toLowerCase().includes(q);
    })
    .sort((a, b) => a.submittedAt - b.submittedAt);

  // Reset the page window whenever the filtered set changes so a
  // new filter doesn't strand the user looking at "items 50-100"
  // of a 12-track result.
  React.useEffect(() => { setVisibleCount(PAGE_SIZE); }, [filter, deferredQuery]);

  const visible = filtered.slice(0, visibleCount);
  const hasMore = visibleCount < filtered.length;

  return (
    <div style={{ padding: '32px 36px 140px', maxWidth: 1280, margin: '0 auto' }}>

      {/* Header: title + summary + search */}
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 22, marginBottom: 22, flexWrap: 'wrap' }}>
        <div style={{ minWidth: 0, flex: '1 1 480px' }}>
          <Eyebrow style={{ marginBottom: 6 }}>
            {counts.all} in Prüfung · älteste zuerst
          </Eyebrow>
          <h1 style={{ fontWeight: 800, fontSize: 40, lineHeight: 1.05, letterSpacing: '-0.02em', margin: 0 }}>
            Zu prüfen
          </h1>
          <p style={{ fontSize: 14.5, color: 'var(--fg-2)', marginTop: 8, maxWidth: 640, opacity: 0.82 }}>
            Ein Track wird automatisch veröffentlicht, sobald er{' '}
            <b style={{ color: 'var(--foam)' }}>{Math.round(ACCEPT_THRESHOLD*100)}%</b>{' '}
            Zustimmung bei mindestens{' '}
            <b style={{ color: 'var(--foam)' }}>{REQUIRED_VOTES}</b> Stimmen erreicht.
            {canForceAccept(me) && ' Du kannst direkt veröffentlichen.'}
          </p>
        </div>
        <div style={{ width: 320, flexShrink: 0 }}>
          <TextInput value={query} onChange={(e) => setQuery(e.target.value)}
            placeholder="Nach Titel oder Künstler suchen…" icon="search"/>
        </div>
      </div>

      {/* Filter chips. Hidden for non-admins/mods who don't see the
          copyright-status detail anyway. */}
      {canForceAccept(me) && (
        <div style={{ display: 'flex', gap: 8, marginBottom: 18, flexWrap: 'wrap' }}>
          <FilterChip active={filter === 'all'} onClick={() => setFilter('all')}>
            Alle <span style={{ opacity: 0.55 }}>· {counts.all}</span>
          </FilterChip>
          <FilterChip active={filter === 'clean'} onClick={() => setFilter('clean')} color="var(--success)">
            Sauber <span style={{ opacity: 0.55 }}>· {counts.clean}</span>
          </FilterChip>
          <FilterChip active={filter === 'flagged'} onClick={() => setFilter('flagged')} color="var(--danger)">
            Geflagged <span style={{ opacity: 0.55 }}>· {counts.flagged}</span>
          </FilterChip>
          <FilterChip active={filter === 'pending-cr'} onClick={() => setFilter('pending-cr')} color="var(--warning)">
            Wird geprüft <span style={{ opacity: 0.55 }}>· {counts.pendingCr}</span>
          </FilterChip>
        </div>
      )}

      {/* Cards or empty state */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        {filtered.length === 0 ? (
          <EmptyState
            icon="circle-check"
            iconColor="var(--success)"
            title={query.trim() ? `Keine Treffer für „${query}"` : 'Nichts zu prüfen'}
            description={query.trim()
              ? 'Versuch einen anderen Suchbegriff.'
              : 'Aktuell stehen keine Tracks zur Prüfung.'}
          />
        ) : visible.map(sub => (
          <SubmissionCard
            key={sub.id}
            sub={sub} me={me}
            isPlaying={audio.currentId === sub.id && audio.isPlaying}
            expanded={expandedId === sub.id}
            onToggle={() => setExpandedId(id => id === sub.id ? null : sub.id)}
            onPlay={() => onPlay(sub)}
            onVote={onVote}
            onForceAccept={onForceAccept}
            onForceReject={onForceReject}
            onBlockUser={onBlockUser}
            onCopyrightSkip={onCopyrightSkip}
            refresh={refresh}
          />
        ))}

        {hasMore && (
          <Glass radius={20} padding="14px 22px" style={{
            display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 14,
          }}>
            <span style={{ fontSize: 12, color: 'var(--fg-3)' }}>
              <span style={{ fontVariantNumeric: 'tabular-nums', color: 'var(--fg-2)' }}>
                {visibleCount}
              </span>
              {' '}von{' '}
              <span style={{ fontVariantNumeric: 'tabular-nums', color: 'var(--fg-2)' }}>
                {numDE(filtered.length)}
              </span>
              {' '}sichtbar
            </span>
            <Button variant="ghost" size="sm" iconRight="chevron-down"
              onClick={() => setVisibleCount(c => Math.min(filtered.length, c * 2))}>
              Mehr anzeigen
            </Button>
          </Glass>
        )}
      </div>
    </div>
  );
}

// Filter chip used to narrow the queue by copyright status. Active
// state is a coloured tint; inactive sits transparent with a soft
// hairline border so the chips don't compete with the cards visually.
function FilterChip({ active, onClick, color = 'var(--lila-300)', children }) {
  return (
    <button onClick={onClick} style={{
      padding: '7px 14px', borderRadius: 999, cursor: 'pointer',
      background: active ? `${color}26` : 'transparent',
      border: active ? `1px solid ${color}66` : '1px solid var(--border-soft)',
      color: active ? color : 'var(--fg-2)',
      fontSize: 12.5, fontWeight: 700, letterSpacing: 0.3,
      fontFamily: 'inherit',
      transition: 'all 150ms var(--ease-out)',
    }}>
      {children}
    </button>
  );
}

window.ReviewQueue = ReviewQueue;
