import { useEffect, useRef, useState } from 'react'
import { api, upload, runJob } from '../api'
import { useJob, ProgressBox, ErrorBox } from '../hooks'
import PromptPanel from '../components/PromptPanel'

const PROVIDER_LABEL = {
  gemini_tts: 'Gemini (natural)',
  elevenlabs: 'ElevenLabs (most human)',
  edge_tts: 'edge-tts (free/robotic)',
}

export default function MediaStep({ reelData, refreshReel, reelId, projectId, setStep }) {
  const base = `/api/projects/${projectId}/reels/${reelId}`
  const [providers, setProviders] = useState([])
  const [provider, setProvider] = useState(reelData?.voice?.provider || '')
  const [voices, setVoices] = useState([])
  const [voice, setVoice] = useState(reelData?.voice?.voice || '')
  const [elevenUsage, setElevenUsage] = useState(null)  // ElevenLabs credit usage
  const [busyLine, setBusyLine] = useState(null)
  const [measured, setMeasured] = useState({})  // {candId: real seconds, read from the <video>}
  const [error, setError] = useState('')
  const [previewing, setPreviewing] = useState(false)
  const [tracks, setTracks] = useState([])
  const [musicBusy, setMusicBusy] = useState(false)
  const [musicQuery, setMusicQuery] = useState('')
  const [musicResults, setMusicResults] = useState([])
  const [musicEnabled, setMusicEnabled] = useState(true)
  const [searching, setSearching] = useState(false)
  const musicInput = useRef(null)
  const voiceJob = useJob()
  // --- Custom voice (ElevenLabs clone / design) ---
  const [voiceMaker, setVoiceMaker] = useState(false)
  const [vmMode, setVmMode] = useState('clone')
  const [vmName, setVmName] = useState('')
  const [vmDesc, setVmDesc] = useState('')
  const [vmBusy, setVmBusy] = useState(false)
  const [vmPreviews, setVmPreviews] = useState([])
  const vmInput = useRef(null)
  const refreshVoices = async (selectName) => {
    const v = await api.get('/api/voices?provider=elevenlabs')
    setVoices(v.voices || [])
    if (selectName) setVoice(selectName)
  }
  const cloneVoice = async (file) => {
    if (!file || !vmName.trim()) { setError('Give the voice a name and pick an audio sample.'); return }
    setVmBusy(true); setError('')
    try {
      const fd = new FormData(); fd.append('name', vmName.trim()); fd.append('description', vmDesc); fd.append('file', file)
      const r = await upload('/api/voices/clone', fd)
      await refreshVoices(r.name); setVoiceMaker(false)
    } catch (e) { setError(e.message) } finally { setVmBusy(false) }
  }
  const designVoice = async () => {
    if (!vmDesc.trim()) { setError('Describe the voice you want.'); return }
    setVmBusy(true); setError('')
    try { const r = await api.post('/api/voices/design', { description: vmDesc.trim() }); setVmPreviews(r.previews || []) }
    catch (e) { setError(e.message) } finally { setVmBusy(false) }
  }
  const saveDesigned = async (gid) => {
    if (!vmName.trim()) { setError('Name the voice before saving.'); return }
    setVmBusy(true); setError('')
    try {
      const r = await api.post('/api/voices/create', { name: vmName.trim(), description: vmDesc.trim(), generated_voice_id: gid })
      await refreshVoices(r.name); setVmPreviews([]); setVoiceMaker(false)
    } catch (e) { setError(e.message) } finally { setVmBusy(false) }
  }
  // Refresh ElevenLabs credit usage whenever it's the selected provider (and after synth).
  useEffect(() => {
    if (provider === 'elevenlabs') api.get('/api/elevenlabs/usage').then(setElevenUsage).catch(() => {})
  }, [provider, reelData?.voice])
  const mediaJob = useJob()

  // Play a short sample of the selected provider+voice (cached server-side).
  const previewVoice = async () => {
    if (!voice) return
    setPreviewing(true)
    try {
      const { url } = await api.post('/api/voices/sample', { provider, voice })
      const audio = new Audio(url)
      audio.onended = () => setPreviewing(false)
      audio.onerror = () => { setError('Could not play voice sample'); setPreviewing(false) }
      await audio.play()
    } catch (e) {
      setError(e.message)
      setPreviewing(false)
    }
  }

  // Load available providers + default voices once.
  useEffect(() => {
    api.get('/api/voices')
      .then((v) => {
        setProviders(v.providers || [])
        if (!provider) { setProvider(v.provider); setVoices(v.voices); if (!voice) setVoice(v.voices[0] || '') }
      })
      .catch((e) => setError(`Voice list unavailable: ${e.message}`))
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  // Reload voices when the provider changes.
  const changeProvider = async (p) => {
    setProvider(p)
    try {
      const v = await api.get(`/api/voices?provider=${encodeURIComponent(p)}`)
      setVoices(v.voices)
      setVoice(v.voices[0] || '')
    } catch (e) { setError(e.message) }
  }

  const runVoice = async () => {
    try {
      await voiceJob.start(`${base}/voice`, { voice, provider })
      await refreshReel()
    } catch { /* shown */ }
  }

  const runMedia = async () => {
    try {
      await mediaJob.start(`${base}/media`)
      await refreshReel()
    } catch { /* shown */ }
  }

  // Load music tracks (with preview URLs) once.
  const [items, setItems] = useState([])
  useEffect(() => { api.get('/api/music').then((d) => { setTracks(d.tracks || []); setItems(d.items || []) }).catch(() => {}) }, [])
  const audioRef = useRef(null)
  const [previewUrl, setPreviewUrl] = useState('')
  const previewTrack = (url) => {
    if (audioRef.current) { audioRef.current.pause(); audioRef.current = null }
    if (previewUrl === url) { setPreviewUrl(''); return }  // clicking the playing one stops it
    const a = new Audio(url); a.volume = vol ?? 0.5; a.onended = () => setPreviewUrl('')
    a.play().catch(() => {}); audioRef.current = a; setPreviewUrl(url)
  }

  const selectClip = async (lineIndex, index) => {
    setBusyLine(lineIndex)
    try {
      await api.post(`${base}/media/select`, { line_index: lineIndex, index })
      await refreshReel()
    } catch (e) { setError(e.message) } finally { setBusyLine(null) }
  }

  const uploadClip = async (lineIndex, file) => {
    if (!file) return
    setBusyLine(lineIndex)
    try {
      const fd = new FormData()
      fd.append('line_index', lineIndex)
      fd.append('file', file)
      await upload(`${base}/media/upload-clip`, fd)
      await refreshReel()
    } catch (e) { setError(e.message) } finally { setBusyLine(null) }
  }

  const [videogen, setVideogen] = useState(false)
  const [genMsg, setGenMsg] = useState('')
  useEffect(() => { api.get('/api/health').then((h) => setVideogen(!!h.videogen)).catch(() => {}) }, [])
  const [clipPrompts, setClipPrompts] = useState({})  // {lineIndex: AI-video prompt override}
  const generateClip = async (lineIndex) => {
    setBusyLine(lineIndex); setError(''); setGenMsg('Starting AI generation…')
    try {
      await runJob(`${base}/media/generate-clip`, { line_index: lineIndex, prompt: clipPrompts[lineIndex] ?? null }, (p) => setGenMsg(p[p.length - 1] || 'Generating…'))
      await refreshReel()
    } catch (e) { setError(e.message) } finally { setBusyLine(null); setGenMsg('') }
  }

  // Ask Gemini to write a detailed AI-video prompt for every scene in one call,
  // then pre-fill each scene's prompt box so you can generate clips from them.
  const [promptsBusy, setPromptsBusy] = useState(false)
  const [promptsOpen, setPromptsOpen] = useState({})  // {lineIndex: bool} — reveal the AI prompt panels
  const writeAllPrompts = async () => {
    setPromptsBusy(true); setError(''); setGenMsg('Writing AI-video prompts for all scenes…')
    try {
      const res = await runJob(`${base}/media/ai-prompts`, {}, (p) => setGenMsg(p[p.length - 1] || 'Writing…'))
      const prompts = res?.prompts || {}
      const byLine = Object.fromEntries(Object.entries(prompts).map(([k, v]) => [Number(k), v]))
      setClipPrompts((prev) => ({ ...prev, ...byLine }))
      // Auto-open each scene's prompt panel so the generated prompts are visible.
      setPromptsOpen((prev) => ({ ...prev, ...Object.fromEntries(Object.keys(byLine).map((k) => [k, true])) }))
    } catch (e) { setError(e.message) } finally { setPromptsBusy(false); setGenMsg('') }
  }

  const pickMusic = async (filename) => {
    setMusicBusy(true)
    try {
      await api.put(`${base}/music`, { filename })
      await refreshReel()
    } catch (e) { setError(e.message) } finally { setMusicBusy(false) }
  }

  const [vol, setVol] = useState(null) // null until media loaded
  useEffect(() => {
    if (reelData?.media && vol === null) setVol(reelData.media.music_volume ?? 0.15)
  }, [reelData?.media, vol])
  const saveVolume = async (v) => {
    try { await api.put(`${base}/music`, { volume: v }); await refreshReel() } catch (e) { setError(e.message) }
  }

  const searchMusic = async () => {
    setSearching(true)
    setError('')
    try {
      const r = await api.get(`/api/music/search?q=${encodeURIComponent(musicQuery)}`)
      setMusicResults(r.results || [])
      setMusicEnabled(r.enabled)
      if (!r.enabled) setError('Set JAMENDO_CLIENT_ID in .env to search free music.')
    } catch (e) { setError(e.message) } finally { setSearching(false) }
  }

  const useTrack = async (track) => {
    setMusicBusy(true)
    try {
      const res = await api.post('/api/music/import', { track })
      setTracks(res.tracks || []); setItems(res.items || [])
      await pickMusic(res.filename) // import + select
      setMusicResults([])
    } catch (e) { setError(e.message); setMusicBusy(false) }
  }

  const uploadMusic = async (file) => {
    if (!file) return
    setMusicBusy(true)
    try {
      const fd = new FormData()
      fd.append('file', file)
      const res = await upload('/api/music/upload', fd)
      setTracks(res.tracks || []); setItems(res.items || [])
      await pickMusic(res.filename) // auto-select what was just uploaded
    } catch (e) { setError(e.message); setMusicBusy(false) }
  }
  const [musicGenPrompt, setMusicGenPrompt] = useState('')
  const [musicGenLen, setMusicGenLen] = useState(20)
  const generateMusic = async () => {
    if (!musicGenPrompt.trim()) return
    setMusicBusy(true); setError('')
    try {
      const res = await api.post('/api/music/generate', { prompt: musicGenPrompt.trim(), length_sec: musicGenLen })
      setTracks(res.tracks || []); setItems(res.items || [])
      await pickMusic(res.filename); setMusicGenPrompt('')
    } catch (e) { setError(e.message) } finally { setMusicBusy(false) }
  }

  const lineLabel = (idx) => {
    const line = (reelData?.voice?.lines || []).find((l) => l.index === idx)
    return line ? line.text : `Line ${idx + 1}`
  }

  return (
    <div>
      <h2>Voice &amp; media</h2>
      <p className="subtitle">Synthesize the narration first — its true timing drives everything — then gather clips per scene.</p>

      <ErrorBox error={error || voiceJob.error || mediaJob.error} />

      <div className="card">
        <div className="row">
          <label className="row" style={{ gap: 8 }}>
            <span className="muted">Voice provider:</span>
            <select value={provider} onChange={(e) => changeProvider(e.target.value)}>
              {providers.map((p) => <option key={p} value={p}>{PROVIDER_LABEL[p] || p}</option>)}
            </select>
          </label>
          <label className="row grow" style={{ gap: 8 }}>
            <span className="muted">Voice:</span>
            <select className="grow" value={voice} onChange={(e) => setVoice(e.target.value)}>
              {voices.map((v) => <option key={v} value={v}>{v}</option>)}
            </select>
          </label>
          <button className="btn secondary" onClick={previewVoice} disabled={previewing || !voice}
            title="Hear a short sample of this voice">
            {previewing ? '♪ Playing…' : '▶ Preview'}
          </button>
          <button className="btn" onClick={runVoice} disabled={voiceJob.running}>
            {voiceJob.running ? 'Synthesizing…' : reelData?.voice ? '↻ Re-synthesize' : '1. Synthesize voice'}
          </button>
        </div>
        {provider === 'elevenlabs' && (() => {
          const scriptChars = (reelData?.script?.scenes || []).reduce((n, s) => n + (s.narration || '').length, 0)
          const cost = Math.round(scriptChars * 0.5)  // turbo_v2_5 ≈ 0.5 credit/char
          const u = elevenUsage
          const short = u?.available && u.limit > 0 && cost > u.remaining
          return (
            <div className="muted" style={{ marginTop: 4, fontSize: 13 }}>
              {scriptChars > 0 && (
                <p style={{ margin: 0 }}>
                  📝 <strong>This synthesis:</strong> {scriptChars.toLocaleString()} chars → ≈ <strong>{cost.toLocaleString()} credits</strong> (turbo bills ~½/char)
                  {short && <span style={{ color: 'var(--bad)' }}> · ⚠ exceeds your remaining credits</span>}
                </p>
              )}
              {u?.available && u.limit > 0 && (
                <p style={{ margin: 0 }}>
                  ⚡ <strong>Balance:</strong> {u.used.toLocaleString()} / {u.limit.toLocaleString()} used · {u.remaining.toLocaleString()} left
                  {' '}({Math.max(0, 100 - Math.round((u.used / u.limit) * 100))}%){u.tier ? ` · ${u.tier}` : ''}
                </p>
              )}
            </div>
          )
        })()}
        {provider === 'elevenlabs' && !providers.includes('elevenlabs') && (
          <p className="muted">Add an ELEVENLABS_API_KEY in .env to enable ElevenLabs.</p>
        )}
        {provider === 'elevenlabs' && providers.includes('elevenlabs') && (
          <div style={{ marginTop: 6 }}>
            <button className="btn small secondary" onClick={() => setVoiceMaker((v) => !v)}>
              {voiceMaker ? '× Close' : '＋ Create a custom voice (clone / design)'}
            </button>
            {voiceMaker && (
              <div className="card" style={{ marginTop: 6, background: 'var(--panel-2)' }}>
                <div className="row" style={{ gap: 6 }}>
                  <button className={`btn small ${vmMode === 'clone' ? '' : 'secondary'}`} onClick={() => setVmMode('clone')}>Clone from sample</button>
                  <button className={`btn small ${vmMode === 'design' ? '' : 'secondary'}`} onClick={() => setVmMode('design')}>Design from description</button>
                </div>
                <input type="text" className="grow" style={{ marginTop: 8 }} placeholder="Voice name (e.g. My Voice)"
                  value={vmName} onChange={(e) => setVmName(e.target.value)} />
                {vmMode === 'clone' ? (
                  <div style={{ marginTop: 6 }}>
                    <p className="muted" style={{ margin: '0 0 6px', fontSize: 12 }}>Upload ~30s–1min of clean speech (your own voice, or one you have rights to). It becomes a narration voice.</p>
                    <input ref={vmInput} type="file" accept="audio/*" style={{ display: 'none' }} onChange={(e) => cloneVoice(e.target.files?.[0])} />
                    <button className="btn small" disabled={vmBusy || !vmName.trim()} onClick={() => vmInput.current?.click()}>
                      {vmBusy ? 'Cloning…' : '⬆ Upload sample & clone'}
                    </button>
                  </div>
                ) : (
                  <div style={{ marginTop: 6 }}>
                    <input type="text" className="grow" placeholder="Describe the voice — e.g. 'energetic gravelly male sports announcer'"
                      value={vmDesc} onChange={(e) => setVmDesc(e.target.value)} />
                    <button className="btn small" style={{ marginTop: 6 }} disabled={vmBusy || !vmDesc.trim()} onClick={designVoice}>
                      {vmBusy ? 'Designing…' : '✨ Generate previews'}
                    </button>
                    {vmPreviews.map((p, i) => (
                      <div key={p.generated_voice_id || i} className="row" style={{ marginTop: 6, gap: 6 }}>
                        <span className="muted">Preview {i + 1}</span>
                        {p.audio_base_64 && <audio src={`data:audio/mpeg;base64,${p.audio_base_64}`} controls preload="none" style={{ height: 30 }} />}
                        <button className="btn small" disabled={vmBusy || !vmName.trim()} onClick={() => saveDesigned(p.generated_voice_id)}>Use this</button>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            )}
          </div>
        )}
        {voiceJob.running && <div style={{ marginTop: 10 }}><ProgressBox progress={voiceJob.progress.length ? voiceJob.progress : ['Starting…']} /></div>}
        {reelData?.voice && (
          <p className="muted" style={{ marginBottom: 0 }}>
            ✓ {reelData.voice.lines.length} lines · {reelData.voice.total_duration_sec.toFixed(1)}s · {reelData.voice.provider} / {reelData.voice.voice || 'default'}
          </p>
        )}
      </div>

      <div className="card">
        <div className="row">
          <span className="grow muted">Stock clips per scene (pick a thumbnail, or upload your own), SFX, and background music.</span>
          <button className="btn" onClick={runMedia} disabled={!reelData?.voice || mediaJob.running}>
            {mediaJob.running ? 'Gathering…' : reelData?.media ? '↻ Re-gather media' : '2. Gather media'}
          </button>
        </div>
        {!reelData?.voice && <p className="muted">Synthesize the voice first.</p>}
        {mediaJob.running && <div style={{ marginTop: 10 }}><ProgressBox progress={mediaJob.progress.length ? mediaJob.progress : ['Starting…']} /></div>}
      </div>

      {reelData?.media && (
        <>
          {videogen && (
            <div className="row" style={{ margin: '4px 0 12px', gap: 8, alignItems: 'center' }}>
              <button className="btn small" onClick={writeAllPrompts} disabled={promptsBusy || busyLine !== null}>
                {promptsBusy ? <span className="spinner" /> : '✨'} Write AI-video prompts (all scenes)
              </button>
              <span className="muted" style={{ fontSize: 12 }}>
                Then hit “Generate” on any scene. AI clips are free but best-effort (~1–3 min each).
              </span>
            </div>
          )}
          {reelData.media.scenes.map((sm) => {
            const sel = sm.candidates[sm.selected]
            const event = (reelData.script?.scenes || []).find((s) => s.visual_query === sm.query)?.event_info
            return (
              <div key={sm.line_index} className="card">
                <p style={{ fontSize: 14, margin: '0 0 2px' }}><strong>Scene {sm.line_index + 1}</strong> — {lineLabel(sm.line_index)}</p>
                <p className="muted" style={{ margin: '0 0 6px' }}>
                  search: “{sm.query}”{sm.sfx_path ? ' · 🔊 SFX' : ''}
                  {sel?.credit ? ` · using: ${sel.credit}` : ''}
                </p>
                {event && <p className="muted" style={{ margin: '0 0 10px', color: 'var(--warn)' }}>📌 Real event: {event}</p>}
                <div className="clip-picker">
                  {sm.candidates.map((c, idx) => (
                    <button key={c.id} className={`clip-option ${idx === sm.selected ? 'chosen' : ''}`}
                      disabled={busyLine === sm.line_index}
                      onClick={() => selectClip(sm.line_index, idx)}
                      title={c.credit}>
                      {c.local_url
                        ? <video src={c.local_url} muted loop
                            onMouseEnter={(e) => e.target.play()} onMouseLeave={(e) => { e.target.pause(); e.target.currentTime = 0 }} />
                        : c.preview_url
                          ? <img src={c.preview_url} alt="" />
                          : <span className="clip-blank">clip</span>}
                      {idx === sm.selected && <span className="chosen-badge">✓</span>}
                      {c.source === 'upload' && <span className="src-badge">yours</span>}
                      {(measured[c.id] || c.duration_sec) > 0 && (
                        <span style={{ position: 'absolute', bottom: 4, right: 4, background: 'rgba(0,0,0,0.7)',
                          color: '#fff', fontSize: 10, padding: '1px 5px', borderRadius: 4 }}>
                          {(measured[c.id] || c.duration_sec).toFixed(1)}s
                        </span>
                      )}
                    </button>
                  ))}
                  <label className="clip-option upload-tile">
                    <input type="file" accept="video/*" style={{ display: 'none' }}
                      disabled={busyLine === sm.line_index}
                      onChange={(e) => uploadClip(sm.line_index, e.target.files?.[0])} />
                    {busyLine === sm.line_index ? <span className="spinner" /> : <span>⬆<br />Upload</span>}
                  </label>
                  {videogen && (
                    <button className="clip-option upload-tile" disabled={busyLine === sm.line_index}
                      title={`Generate a clip from: ${sm.query}`} onClick={() => generateClip(sm.line_index)}>
                      {busyLine === sm.line_index ? <span className="spinner" /> : <span>✨<br />Generate</span>}
                    </button>
                  )}
                </div>
                {busyLine === sm.line_index && genMsg && <p className="muted" style={{ margin: '4px 0 0' }}>{genMsg}</p>}
                {videogen && (
                  <PromptPanel label="AI-video" disabled={busyLine === sm.line_index}
                    value={clipPrompts[sm.line_index] ?? null}
                    setValue={(v) => setClipPrompts((p) => ({ ...p, [sm.line_index]: v }))}
                    open={!!promptsOpen[sm.line_index]}
                    setOpen={(v) => setPromptsOpen((p) => ({ ...p, [sm.line_index]: v }))}
                    fetchPrompt={async () => (await runJob(`${base}/media/clip-prompt`, { line_index: sm.line_index })).prompt} />
                )}
                {sel?.local_url && (
                  <div className="row" style={{ marginTop: 10, gap: 12, alignItems: 'flex-start' }}>
                    <video key={sel.local_url} src={sel.local_url} controls playsInline preload="metadata"
                      onLoadedMetadata={(e) => setMeasured((m) => ({ ...m, [sel.id]: e.target.duration }))}
                      style={{ width: 150, aspectRatio: '9/16', objectFit: 'cover', borderRadius: 8, background: '#000' }} />
                    <div className="muted" style={{ fontSize: 13 }}>
                      <div><strong>Selected clip — full, untrimmed</strong></div>
                      <div>Length: {(measured[sel.id] || sel.duration_sec || 0).toFixed(1)}s</div>
                      <div>This scene needs {((reelData?.voice?.lines || []).find((l) => l.index === sm.line_index)?.duration_sec || 0).toFixed(1)}s of it.</div>
                      <div style={{ marginTop: 4 }}>Play it here to confirm the moment is in the clip. Choose <em>where</em> that slice starts on the Effects/preview step.</div>
                    </div>
                  </div>
                )}
              </div>
            )
          })}

          {/* Background music */}
          <div className="card">
            <div className="row">
              <strong className="grow">🎵 Background music</strong>
              <button className={`btn small ${!reelData.media.music_path ? '' : 'secondary'}`}
                onClick={() => pickMusic('')}>None</button>
              <input ref={musicInput} type="file" accept="audio/*" style={{ display: 'none' }}
                onChange={(e) => uploadMusic(e.target.files?.[0])} />
              <button className="btn small secondary" disabled={musicBusy}
                onClick={() => musicInput.current?.click()}>⬆ Upload track</button>
              {musicBusy && <span className="spinner" />}
            </div>
            {/* Audition + pick each track */}
            {items.map((it) => {
              const chosen = (reelData.media.music_path || '').endsWith(it.name)
              return (
                <div key={it.name} className={`topic-row ${chosen ? 'selected' : ''}`}>
                  <button className="btn small secondary" title="Preview / stop" onClick={() => previewTrack(it.url)}>
                    {previewUrl === it.url ? '⏹' : '▶'}</button>
                  <span className="grow">{it.name}</span>
                  <button className={`btn small ${chosen ? '' : 'secondary'}`}
                    disabled={musicBusy} onClick={() => pickMusic(it.name)}>{chosen ? '✓ Using' : 'Use'}</button>
                </div>
              )
            })}
            {reelData.media.music_credit && (
              <p className="muted" style={{ margin: '6px 0 0' }}>Credit: {reelData.media.music_credit}</p>
            )}
            {reelData.media.music_path && (
              <div className="row" style={{ marginTop: 8 }}>
                <span className="muted">Volume: {Math.round((vol ?? 0.15) * 100)}%</span>
                <input type="range" min="0" max="1" step="0.01" className="grow"
                  value={vol ?? 0.15}
                  onChange={(e) => setVol(Number(e.target.value))}
                  onMouseUp={(e) => saveVolume(Number(e.target.value))}
                  onTouchEnd={(e) => saveVolume(Number(e.target.value))} />
              </div>
            )}

            {/* Generate music (ElevenLabs) */}
            <div style={{ marginTop: 12, borderTop: '1px solid var(--border)', paddingTop: 12 }}>
              <div className="row">
                <span className="muted">✨ Generate music:</span>
                <input type="text" className="grow" placeholder="e.g. epic orchestral build for a goal montage"
                  value={musicGenPrompt} onChange={(e) => setMusicGenPrompt(e.target.value)}
                  onKeyDown={(e) => e.key === 'Enter' && generateMusic()} />
                <label className="muted" style={{ whiteSpace: 'nowrap' }}>{musicGenLen}s
                  <input type="range" min="5" max="60" step="1" value={musicGenLen} onChange={(e) => setMusicGenLen(Number(e.target.value))} /></label>
                <button className="btn small" onClick={generateMusic} disabled={musicBusy || !musicGenPrompt.trim()}
                  title="Generate a track with ElevenLabs (uses credits). Check commercial-use terms before publishing.">
                  {musicBusy ? '✨ Generating…' : '✨ Generate'}
                </button>
              </div>
              <p className="muted" style={{ margin: '4px 0 0', fontSize: 12 }}>Needs ELEVENLABS_API_KEY · check ElevenLabs’ commercial-use terms before publishing generated music.</p>
            </div>

            {/* Search royalty-free music (Jamendo) */}
            <div style={{ marginTop: 12, borderTop: '1px solid var(--border)', paddingTop: 12 }}>
              <div className="row">
                <span className="muted">🔎 Search free music:</span>
                <input type="text" className="grow" placeholder="e.g. upbeat sport, epic, lo-fi"
                  value={musicQuery} onChange={(e) => setMusicQuery(e.target.value)}
                  onKeyDown={(e) => e.key === 'Enter' && searchMusic()} />
                <button className="btn small secondary" onClick={searchMusic} disabled={searching}>
                  {searching ? 'Searching…' : 'Search'}
                </button>
              </div>
              {!musicEnabled && (
                <p className="muted" style={{ marginBottom: 0 }}>
                  Add a free JAMENDO_CLIENT_ID in .env (developer.jamendo.com) to search royalty-free music in-app.
                </p>
              )}
              {musicResults.map((t) => (
                <div key={t.id} className="topic-row">
                  <div className="grow">
                    <strong>{t.title}</strong> <span className="muted">— {t.artist}</span>
                    <div className="topic-meta">{t.license} · {Math.round(t.duration_sec)}s</div>
                  </div>
                  {t.preview_url && <audio src={t.preview_url} controls preload="none" style={{ height: 32 }} />}
                  <button className="btn small" disabled={musicBusy} onClick={() => useTrack(t)}>Use</button>
                </div>
              ))}
            </div>

            {tracks.length === 0 && musicResults.length === 0 && (
              <p className="muted" style={{ marginBottom: 0, marginTop: 8 }}>
                No tracks yet — search Jamendo above, upload one, or drop files into assets/music/.
                (Trending/licensed audio is added in-app at posting time, never embedded. Credit CC-BY tracks in your post.)
              </p>
            )}
          </div>

          <button className="btn" onClick={() => setStep(5)}>Continue to Effects →</button>
        </>
      )}
    </div>
  )
}
