// Submit — title + cover (palette OR upload) + MP3 dropzone, then post.
//
// All the real work lives server-side: validation, transcoding to
// 96 kbps, CDN upload, kicking off the copyright check + lyrics
// transcription jobs. This screen is just a thin form on top of
// Api.submit().
//
// Gate chain (top to bottom):
//   1. banned        → blocking "Du bist gebannt"
//   2. blocked       → blocking "Vom Hochladen gesperrt"
//   3. <100h playtime → soft "Noch nicht ganz so weit"
//   4. no artist profile → onboarding form (sets stage name + bio)
//   5. posted=true   → success state
//   6. default       → the actual upload form

// Outer wrapper. For non-admins it's a pass-through to the single-
// track form; for admins it adds a small mode-toggle at the top so
// they can switch between "Einzelner Track" and "Bulk-Upload"
// without leaving this route. Keeps the nav lean (no separate
// /bulk tab) and bulk machinery out of view for regular users.
function SubmitScreen({ me, setRoute, refresh }) {
  const [mode, setMode] = React.useState('single');   // 'single' | 'bulk'
  const adminBulk = canForceAccept(me);

  // Render the toggle BAR — only for admins, only when the page is
  // in a state where the toggle is meaningful (i.e. the single
  // gate-states like banned/blocked/playtime don't render the form
  // anyway, so we skip the toggle there too).
  const toggle = adminBulk ? (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 6,
      margin: '0 auto 24px', maxWidth: 820,
      padding: '4px', borderRadius: 12,
      background: 'var(--bg-input)',
      border: '1px solid var(--border-soft)',
      width: 'fit-content',
    }}>
      <ModeChip active={mode === 'single'} icon="cloud-arrow-up"
        onClick={() => setMode('single')}>
        Einzelner Track
      </ModeChip>
      <ModeChip active={mode === 'bulk'} icon="folder-tree"
        onClick={() => setMode('bulk')}>
        Bulk-Upload
      </ModeChip>
    </div>
  ) : null;

  if (mode === 'bulk' && adminBulk) {
    return (
      <>
        {toggle}
        <BulkUploadScreen me={me} refresh={refresh}/>
      </>
    );
  }

  return (
    <>
      {toggle}
      <SingleSubmitScreen me={me} setRoute={setRoute} refresh={refresh}/>
    </>
  );
}

// Sub-tab chip used by the admin mode toggle. Tabbed-pill look,
// matches the visual style of the home library's filter chips +
// the stats screen's sub-tab toggles so the affordance reads
// consistently across screens.
function ModeChip({ active, icon, onClick, children }) {
  return (
    <button onClick={onClick} style={{
      display: 'inline-flex', alignItems: 'center', gap: 7,
      padding: '8px 16px', borderRadius: 10,
      border: 'none', cursor: 'pointer',
      background: active ? 'rgba(169,114,244,0.18)' : 'transparent',
      color: active ? 'var(--lila-200)' : 'var(--fg-2)',
      fontSize: 13, fontWeight: 700, fontFamily: 'inherit',
      transition: 'background 150ms var(--ease-out)',
    }}>
      <ApIcon name={icon} size={12}/>
      {children}
    </button>
  );
}

function SingleSubmitScreen({ me, setRoute, refresh }) {
  const [title, setTitle] = React.useState('');
  const [coverIdx, setCoverIdx] = React.useState(0);
  const [coverFile, setCoverFile] = React.useState(null);
  // Raw pre-crop image — fed into the cropper modal. After cropping,
  // the result lands in coverFile and gets uploaded.
  const [rawCoverFile, setRawCoverFile] = React.useState(null);
  const [mp3, setMp3] = React.useState(null);
  const [mp3Url, setMp3Url] = React.useState(null);
  const [mp3Drag, setMp3Drag] = React.useState(false);
  const [coverDrag, setCoverDrag] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [posted, setPosted] = React.useState(false);
  const [err, setErr] = React.useState('');

  // Object-URL lifecycle for the audio + cover previews. Without
  // revocation these slowly leak across many file picks.
  React.useEffect(() => {
    if (!mp3) { setMp3Url(null); return; }
    const url = URL.createObjectURL(mp3);
    setMp3Url(url);
    return () => URL.revokeObjectURL(url);
  }, [mp3]);

  const coverPreviewUrl = React.useMemo(
    () => coverFile ? URL.createObjectURL(coverFile) : null,
    [coverFile],
  );
  React.useEffect(() => {
    if (!coverPreviewUrl) return;
    return () => URL.revokeObjectURL(coverPreviewUrl);
  }, [coverPreviewUrl]);

  // Beachy-Lila palette set. 9 gradients sampled from the brand's
  // colour ramps — every artist's first cover defaults to the lila-
  // coral combo (index 0). Picking one only matters until the user
  // uploads a real image; then `coverFile` overrides the palette.
  const palettes = [
    ['#FF7E8B', '#8A4FE2'],  ['#6EE0D2', '#2BB0C8'],  ['#FFD27A', '#FF7E8B'],
    ['#A972F4', '#6F39C9'],  ['#E94CD0', '#8A4FE2'],  ['#FFD27A', '#FFB48A'],
    ['#8FB7FF', '#A972F4'],  ['#6EE0D2', '#8FB7FF'],  ['#FF7E8B', '#FFD27A'],
  ];

  // ── Gate states ────────────────────────────────────────────────

  if (isBanned(me)) {
    return (
      <BlockCard icon="x" iconColor="var(--danger)" title="Du bist gebannt">
        Solange dein Bann aktiv ist, kannst du keine Tracks hochladen.
        Wenn das ein Fehler ist, melde dich beim Team.
      </BlockCard>
    );
  }

  if (isBlocked(me)) {
    return (
      <BlockCard icon="shield" iconColor="var(--danger)" title="Vom Hochladen gesperrt">
        Ein Admin hat dich vom Hochladen gesperrt. Auf dem Server selbst
        bist du nicht eingeschränkt — nur neue Tracks gehen aktuell
        nicht. Melde dich beim Team, falls das ein Fehler ist.
        {/* Block details — only render when the server actually
            shipped a blockInfo. Older sessions or race conditions
            might leave it null; the generic copy above still reads
            sensibly without it. */}
        {me.blockInfo && (
          <div style={{
            marginTop: 18, padding: '12px 14px', borderRadius: 12,
            background: 'rgba(255,110,138,0.10)',
            border: '1px solid rgba(255,110,138,0.3)',
            textAlign: 'left',
          }}>
            <Eyebrow style={{ marginBottom: 6, color: 'var(--danger)' }}>Grund</Eyebrow>
            <div style={{ fontSize: 13.5, color: 'var(--foam)', lineHeight: 1.5 }}>
              {me.blockInfo.reason || <span style={{ color: 'var(--fg-3)', fontStyle: 'italic' }}>Kein Grund angegeben</span>}
            </div>
            <div style={{ fontSize: 11.5, color: 'var(--fg-3)', marginTop: 8 }}>
              Gesperrt {timeAgo(me.blockInfo.blockedAt)}
            </div>
          </div>
        )}
      </BlockCard>
    );
  }

  if (!canSubmit(me)) {
    return (
      <BlockCard icon="clock" iconColor="var(--warning)" title="Noch nicht ganz so weit">
        Du hast aktuell <b>{me.playtime}h</b> auf dem Server. Ab{' '}
        <b>100 Stunden</b> kannst du eigene Tracks hochladen.
      </BlockCard>
    );
  }

  // Eligible but no stage name yet → onboarding. ArtistProfileForm
  // saves via Api.artist.putMe(); `onSaved={refresh}` re-fetches /me
  // so this screen re-renders with hasArtistProfile(me) === true.
  if (!hasArtistProfile(me)) {
    return <ArtistProfileForm me={me} mode="onboarding" onSaved={refresh}/>;
  }

  if (posted) {
    return (
      <div style={{ padding: '60px 32px 140px', maxWidth: 620, margin: '0 auto' }}>
        <Glass radius={28} padding={48} style={{ textAlign: 'center' }} orb="var(--success)">
          <div style={{
            width: 76, height: 76, borderRadius: '50%', margin: '0 auto 18px',
            background: 'rgba(95,216,166,0.16)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            border: '1px solid rgba(95,216,166,0.4)',
          }}>
            <ApIcon name="check" size={34} color="var(--success)"/>
          </div>
          <h2 style={{ fontWeight: 800, fontSize: 28, letterSpacing: '-0.02em', margin: '0 0 10px' }}>
            Track ist hochgeladen
          </h2>
          <p style={{ fontSize: 15, color: 'var(--fg-2)', lineHeight: 1.55, margin: '0 0 26px', opacity: 0.85 }}>
            Dein Track liegt jetzt zur Prüfung. Sobald entschieden wurde,
            siehst du das in deinem Profil.
          </p>
          <div style={{ display: 'flex', gap: 10, justifyContent: 'center' }}>
            <Button variant="secondary" size="md" onClick={() => setRoute('me')}>
              Zum Profil
            </Button>
            <Button variant="primary" size="md" onClick={() => {
              setPosted(false); setTitle(''); setMp3(null); setCoverFile(null);
            }}>
              Noch einen hochladen
            </Button>
          </div>
        </Glass>
      </div>
    );
  }

  // ── Submit handler ─────────────────────────────────────────────

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!title.trim() || !mp3) return;
    setBusy(true); setErr('');
    try {
      const form = new FormData();
      form.append('title', title.trim());
      form.append('coverGradient', JSON.stringify(palettes[coverIdx]));
      if (coverFile) form.append('cover', coverFile);
      form.append('mp3', mp3);
      await Api.submit(form);
      await refresh();
      setPosted(true);
      toast.success('Track hochgeladen — viel Erfolg!');
    } catch (ex) {
      const msg = ex.message || 'Hochladen fehlgeschlagen.';
      setErr(msg);
      toast.error(msg);
    } finally {
      setBusy(false);
    }
  };

  // Drop handlers reused for both file slots — pick the right slot
  // based on MIME. Reject anything that isn't audio/mpeg or image/*.
  const handleMp3Drop = (e) => {
    e.preventDefault();
    setMp3Drag(false);
    const f = e.dataTransfer.files?.[0];
    if (!f) return;
    if (!/^audio\/(mpeg|mp3)$/i.test(f.type) && !f.name.toLowerCase().endsWith('.mp3')) {
      toast.error('Bitte eine MP3-Datei wählen.');
      return;
    }
    setMp3(f);
  };
  const handleCoverDrop = (e) => {
    e.preventDefault();
    setCoverDrag(false);
    const f = e.dataTransfer.files?.[0];
    if (!f) return;
    if (!/^image\//i.test(f.type)) {
      toast.error('Bitte ein Bild wählen.');
      return;
    }
    // Even square drops go through the cropper so the user gets a
    // chance to re-frame and confirm.
    setRawCoverFile(f);
  };

  // ── Main form ──────────────────────────────────────────────────

  const canPost = title.trim() && mp3 && !busy;

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

      <Eyebrow style={{ marginBottom: 6 }}>Schritt 1 von 1 · automatisch geprüft</Eyebrow>
      <h1 style={{ fontWeight: 800, fontSize: 40, lineHeight: 1.05, letterSpacing: '-0.02em', margin: '0 0 6px' }}>
        Track hochladen
      </h1>
      <p style={{ fontSize: 15, color: 'var(--fg-2)', maxWidth: 580, opacity: 0.85, margin: '0 0 28px' }}>
        Lade deinen Track hoch und gib ihm einen Titel — der Rest läuft automatisch.
        Sobald er freigegeben ist, taucht er ingame auf.
      </p>

      <form onSubmit={handleSubmit}>
        <Glass radius={20} padding={28} style={{ marginBottom: 18 }}>

          {/* Title */}
          <div style={{ marginBottom: 24 }}>
            <Label>Titel</Label>
            <TextInput value={title} onChange={(e) => setTitle(e.target.value)}
              placeholder="z. B. Cinder Bloom" maxLength={80}/>
          </div>

          {/* Cover */}
          <div style={{ marginBottom: 24 }}>
            <Label hint="Eigenes Bild hochladen (wird auf 512×512 WebP komprimiert) — oder einen Farbverlauf als Platzhalter wählen.">
              Cover
            </Label>
            <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>
              {palettes.map((p, i) => {
                const selected = !coverFile && i === coverIdx;
                return (
                  <button key={i} type="button"
                    onClick={() => { setCoverIdx(i); setCoverFile(null); }}
                    aria-label={`Farbverlauf ${i + 1}`}
                    style={{
                      width: 68, height: 68, borderRadius: 14, padding: 0, cursor: 'pointer',
                      background: `linear-gradient(135deg, ${p[0]}, ${p[1]})`,
                      border: selected ? '3px solid var(--coral)' : '3px solid transparent',
                      boxShadow: selected ? 'var(--glow-coral)' : `0 6px 18px ${p[0]}50`,
                      transition: 'all 150ms var(--ease-out)',
                    }}/>
                );
              })}
              <label
                onDragOver={(e) => { e.preventDefault(); setCoverDrag(true); }}
                onDragLeave={() => setCoverDrag(false)}
                onDrop={handleCoverDrop}
                style={{
                  width: 68, height: 68, borderRadius: 14, cursor: 'pointer',
                  background: coverPreviewUrl
                    ? `url(${coverPreviewUrl}) center/cover`
                    : 'var(--bg-input)',
                  border: coverFile
                    ? '3px solid var(--coral)'
                    : coverDrag
                      ? '3px dashed var(--coral)'
                      : '2px dashed var(--border-medium)',
                  display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
                  color: 'var(--fg-3)', gap: 4, fontSize: 10,
                }}
              >
                {!coverFile && (
                  <>
                    <ApIcon name="image" size={18}/>
                    <span>Upload</span>
                  </>
                )}
                <input type="file" accept="image/*" hidden onChange={e => {
                  const f = e.target.files?.[0];
                  if (f) setRawCoverFile(f);
                  e.target.value = '';
                }}/>
              </label>
            </div>
          </div>

          {/* MP3 */}
          <div>
            <Label hint="MP3, bis zu 32 MB. Wird automatisch auf 192 kbps re-encoded für stabile Qualität ingame.">
              Audio-Datei
            </Label>
            <label
              onDragOver={(e) => { e.preventDefault(); setMp3Drag(true); }}
              onDragLeave={() => setMp3Drag(false)}
              onDrop={handleMp3Drop}
              style={{
                display: 'flex', alignItems: 'center', gap: 16, padding: 20, borderRadius: 16,
                background: mp3Drag ? 'rgba(255,126,139,0.10)' : 'var(--bg-input)',
                border: mp3Drag
                  ? '2px dashed var(--coral)'
                  : mp3
                    ? '1px dashed rgba(95,216,166,0.5)'
                    : '1px dashed var(--border-medium)',
                cursor: 'pointer', transition: 'all 150ms var(--ease-out)',
              }}
            >
              <div style={{
                width: 56, height: 56, borderRadius: 14, flexShrink: 0,
                background: mp3 ? 'rgba(95,216,166,0.16)' : 'rgba(196,154,255,0.10)',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
              }}>
                <ApIcon name={mp3 ? 'music' : 'upload'} size={22}
                  color={mp3 ? 'var(--success)' : 'var(--fg-2)'}/>
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{
                  fontSize: 15, fontWeight: 600, color: 'var(--foam)',
                  whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
                }}>
                  {mp3?.name || (mp3Drag ? 'Datei hier loslassen' : 'MP3 auswählen')}
                </div>
                <div style={{ fontSize: 12, color: 'var(--fg-3)', marginTop: 3 }}>
                  {mp3
                    ? `${(mp3.size / 1048576).toFixed(1)} MB · bereit zum Hochladen`
                    : 'oder einfach hierher ziehen'}
                </div>
              </div>
              {mp3 && (
                <button type="button"
                  onClick={(e) => { e.preventDefault(); setMp3(null); }}
                  title="Entfernen"
                  style={{
                    background: 'transparent', border: 'none', cursor: 'pointer',
                    color: 'var(--fg-3)', padding: 6, display: 'flex',
                  }}
                >
                  <ApIcon name="x" size={16}/>
                </button>
              )}
              <input type="file" accept=".mp3,audio/mpeg" hidden
                onChange={e => setMp3(e.target.files?.[0] || null)}/>
            </label>

            {mp3Url && (
              <audio src={mp3Url} controls
                style={{
                  width: '100%', marginTop: 12, borderRadius: 12,
                  filter: 'invert(0.85) hue-rotate(180deg)',
                }}/>
            )}
          </div>
        </Glass>

        {err && (
          <Glass radius={12} padding={14} style={{
            marginBottom: 14, border: '1px solid rgba(255,110,138,0.45)',
            background: 'rgba(255,110,138,0.10)',
          }}>
            <div style={{ color: 'var(--danger)', fontSize: 13 }}>{err}</div>
          </Glass>
        )}

        <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
          <Glass radius={14} padding="10px 14px" style={{
            display: 'flex', alignItems: 'center', gap: 10, fontSize: 12.5,
          }}>
            <ApIcon name="circle-info" size={14} color="var(--lila-300)"/>
            <span style={{ color: 'var(--fg-2)' }}>
              Hochgeladen als <b style={{ color: 'var(--foam)' }}>
                <PlayerName player={me} size={12.5} opacity={1}/>
              </b> · {me.playtime}h Spielzeit
            </span>
          </Glass>
          <div style={{ flex: 1 }}/>
          <Button type="submit" variant="primary" size="lg"
            icon={busy ? null : 'upload'} disabled={!canPost}>
            {busy ? 'Wird hochgeladen…' : 'Track hochladen'}
          </Button>
        </div>
      </form>

      {rawCoverFile && (
        <ImageCropper
          file={rawCoverFile}
          onCancel={() => setRawCoverFile(null)}
          onCrop={(cropped) => {
            setCoverFile(cropped);
            setRawCoverFile(null);
          }}
        />
      )}
    </div>
  );
}

// Reusable centred block-card for the three gate states (banned,
// blocked, not-eligible). All share the same shape — coloured icon
// in a circle, headline, body copy — so we don't repeat the markup.
function BlockCard({ icon, iconColor, title, children }) {
  return (
    <div style={{ padding: '60px 32px 140px', maxWidth: 620, margin: '0 auto' }}>
      <Glass radius={28} padding={48} style={{ textAlign: 'center' }}>
        <div style={{
          width: 76, height: 76, borderRadius: '50%', margin: '0 auto 18px',
          background: `${iconColor.replace('var(--', 'rgba(255,255,255,').slice(0, -1)}, 0.0)`,  // placeholder
          backgroundColor: 'rgba(255,255,255,0.06)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          border: `1px solid ${iconColor}55`,
        }}>
          <ApIcon name={icon} size={32} color={iconColor}/>
        </div>
        <h2 style={{ fontWeight: 800, fontSize: 24, letterSpacing: '-0.02em', margin: '0 0 10px' }}>
          {title}
        </h2>
        <p style={{ fontSize: 14, color: 'var(--fg-2)', lineHeight: 1.55, margin: 0, opacity: 0.85 }}>
          {children}
        </p>
      </Glass>
    </div>
  );
}

window.SubmitScreen = SubmitScreen;
