/* Studio 76 — The Imaginarium. Talk to "Eve", Studio 76's designer, using the
   browser's built-in speech (no external voice key needed): the browser
   listens, Gemini is Eve's brain (imaginarium.php?action=chat), and the
   browser speaks her replies. She renders the piece live with Nano Banana 2.
   Typed flow stays as a fallback. */

const IMAG_EXAMPLES = [
  'A fine rose-gold pendant with a single pearl — modern and minimal',
  'Delicate diamond ear-climbers, contemporary, barely-there',
  'A slim stacking ring with a tiny emerald, new-age and clean',
  'A thread-thin gold chain with one floating sapphire',
];

const IMAG_TONES = [
  { k: 'original', label: 'As designed' },
  { k: 'yellow',   label: 'Yellow gold' },
  { k: 'white',    label: 'White gold' },
  { k: 'rose',     label: 'Rose gold' },
];

async function imagApi(action, payload) {
  const r = await fetch('imaginarium.php', {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(Object.assign({ action: action }, payload)),
  });
  let j = {}; try { j = await r.json(); } catch (e) {}
  if (!r.ok || j.error) throw new Error(j.error || ('Something went wrong (' + r.status + ')'));
  return j;
}

function getEveVoice() {
  const vs = (window.speechSynthesis && window.speechSynthesis.getVoices()) || [];
  if (!vs.length) return null;
  const inFemale = vs.find(v => /en-IN/i.test(v.lang) && /female|woman|priya|veena|raveena|aditi|heera|neerja/i.test(v.name));
  const inAny    = vs.find(v => /en-IN/i.test(v.lang));
  const enFemale = vs.find(v => /^en/i.test(v.lang) && /female|samantha|victoria|karen|tessa|moira|fiona|serena|zira|google uk english female/i.test(v.name));
  const enAny    = vs.find(v => /^en/i.test(v.lang));
  return inFemale || inAny || enFemale || enAny || vs[0];
}

function SpecRow({ label, value }) {
  if (!value) return null;
  return <div className="spec-row"><span className="spec-k">{label}</span><span className="spec-v">{value}</span></div>;
}

function Imaginarium({ onBegin }) {
  const [mode, setMode]   = React.useState('console');   // console | voice
  const [voiceReady, setVoiceReady] = React.useState(false);
  const [vstate, setV]    = React.useState('idle');      // idle | connecting | live | ended | error
  const [speaking, setSpeaking] = React.useState(false);
  const [listening, setListening] = React.useState(false);
  const [interim, setInterim] = React.useState('');
  const [transcript, setTranscript] = React.useState([]);

  const [design, setDesign] = React.useState(null);
  const [hero, setHero]     = React.useState(null);
  const [variants, setVariants] = React.useState({});
  const [wire, setWire]     = React.useState(null);
  const [active, setActive] = React.useState('original');
  const [view, setView]     = React.useState('render');
  const [err, setErr]       = React.useState('');

  const [dream, setDream] = React.useState('');
  const [phase, setPhase] = React.useState('idle');

  const heroRef   = React.useRef(null);
  const tEndRef   = React.useRef(null);
  const taRef     = React.useRef(null);
  const histRef   = React.useRef([]);
  const recogRef  = React.useRef(null);
  const endedRef  = React.useRef(false);
  const audioRef  = React.useRef(null);
  const speakingRef = React.useRef(false);

  React.useEffect(() => { heroRef.current = hero; }, [hero]);
  React.useEffect(() => { if (tEndRef.current) tEndRef.current.scrollTop = tEndRef.current.scrollHeight; }, [transcript, interim]);

  React.useEffect(() => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    setVoiceReady(!!(SR && window.speechSynthesis));
    if (window.speechSynthesis) { try { window.speechSynthesis.getVoices(); window.speechSynthesis.onvoiceschanged = () => window.speechSynthesis.getVoices(); } catch (e) {} }
    return () => { endedRef.current = true; try { if (recogRef.current) recogRef.current.abort(); } catch (e) {} try { window.speechSynthesis && window.speechSynthesis.cancel(); } catch (e) {} };
  }, []);

  const fmtStone = (s) => s ? [s.carat_estimate, s.cut, s.color, s.type].filter(Boolean).join(' ') : '';
  const fmtAccents = (a) => (a && a.length) ? a.map(x => (typeof x === 'string' ? x : [x.cut, x.color, x.type].filter(Boolean).join(' '))).filter(Boolean).join(', ') : '';
  const push = (role, text) => setTranscript(t => t.concat([{ role, text }]));

  /* ---------- render helpers ---------- */
  async function renderExtras(id) {
    setWire('loading');
    try { const w = await imagApi('wireframe', { id }); setWire(w.dataUrl); } catch (e) { setWire(null); }
    for (const t of ['yellow', 'white', 'rose']) {
      setVariants(v => Object.assign({}, v, { [t]: 'loading' }));
      try { const r = await imagApi('variant', { id, tone: t }); setVariants(v => Object.assign({}, v, { [t]: r.dataUrl })); }
      catch (e) { setVariants(v => { const n = Object.assign({}, v); delete n[t]; return n; }); }
    }
  }
  async function renderHero(spec) {
    setHero(null); setVariants({}); setWire(null); setActive('original'); setView('render');
    const h = await imagApi('hero', { spec });
    setHero(h); setVariants({ original: h.dataUrl });
    return h.id;
  }
  function applyVariant(tone) {
    const id = heroRef.current && heroRef.current.id;
    const t = ({ rose: 'rose', yellow: 'yellow', white: 'white' })[tone] || '';
    if (!id || !t) return;
    setActive(t); setView('render'); setVariants(v => Object.assign({}, v, { [t]: 'loading' }));
    imagApi('variant', { id, tone: t }).then(r => setVariants(v => Object.assign({}, v, { [t]: r.dataUrl }))).catch(() => {});
  }

  /* ---------- browser speech ---------- */
  function browserSpeak(text) {
    return new Promise(res => {
      if (!window.speechSynthesis || !text) return res();
      try {
        const u = new SpeechSynthesisUtterance(text);
        const v = getEveVoice(); if (v) { u.voice = v; u.lang = v.lang; } else { u.lang = 'en-IN'; }
        u.rate = 1.0; u.pitch = 1.06;
        u.onend = () => res(); u.onerror = () => res();
        window.speechSynthesis.cancel(); window.speechSynthesis.speak(u);
      } catch (e) { res(); }
    });
  }
  function playAudio(dataUrl) {
    return new Promise(res => {
      try {
        const a = audioRef.current || new Audio(); audioRef.current = a;
        a.muted = false; a.src = dataUrl;
        a.onended = () => res('ok'); a.onerror = () => res('err');
        const p = a.play(); if (p && p.catch) p.catch(() => res('err'));
      } catch (e) { res('err'); }
    });
  }
  async function speak(text) {
    if (!text) return;
    speakingRef.current = true; setSpeaking(true);
    let done = false;
    try { const r = await imagApi('say', { text }); if (r && r.audio) { const out = await playAudio(r.audio); done = (out === 'ok'); } } catch (e) {}
    if (!done && !endedRef.current) { await browserSpeak(text); }   // fallback: browser voice
    speakingRef.current = false; setSpeaking(false);
  }
  function listenOnce() {
    return new Promise(res => {
      const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
      if (!SR) return res('');
      const r = new SR(); recogRef.current = r;
      r.lang = 'en-IN'; r.interimResults = true; r.maxAlternatives = 1; r.continuous = false;
      let finalText = '';
      r.onresult = (e) => {
        let intr = '';
        for (let i = e.resultIndex; i < e.results.length; i++) {
          const seg = e.results[i]; if (seg.isFinal) finalText += seg[0].transcript; else intr += seg[0].transcript;
        }
        setInterim(intr);
      };
      r.onerror = () => {};
      r.onend = () => { setListening(false); setInterim(''); res(finalText.trim()); };
      setListening(true);
      try { r.start(); } catch (e) { setListening(false); res(''); }
    });
  }

  function waitSpeak() { return new Promise(res => { const c = () => { if (endedRef.current || !speakingRef.current) res(); else setTimeout(c, 150); }; c(); }); }
  async function runConversation() {
    let empties = 0;
    while (!endedRef.current) {
      await waitSpeak();              // let Eve finish before we listen (no echo)
      if (endedRef.current) break;
      const said = await listenOnce();
      if (endedRef.current) break;
      if (!said) { empties++; if (empties >= 3) { const n = 'I’m still here whenever you’re ready.'; push('eve', n); await speak(n); empties = 0; } continue; }
      empties = 0;
      push('you', said); histRef.current.push({ role: 'you', text: said });
      let r;
      try { r = await imagApi('chat', { history: histRef.current }); }
      catch (e) { const m = 'Sorry, I lost that for a moment — could you say it again?'; push('eve', m); await speak(m); continue; }
      push('eve', r.reply); histRef.current.push({ role: 'eve', text: r.reply });
      if (r.ready && r.spec) {
        setDesign({ concept: r.concept || { name: 'Your piece', description: '' }, spec: r.spec });
        (async () => { try { const id = await renderHero(r.spec); renderExtras(id); } catch (e) {} })();
      }
      if (r.variant) applyVariant(r.variant);
      if (endedRef.current) break;
      await speak(r.reply);
    }
  }

  async function startVoice() {
    setErr('');
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR || !window.speechSynthesis) {
      setVoiceReady(false);
      if (taRef.current) { taRef.current.focus(); taRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' }); }
      return;
    }
    setMode('voice'); setV('connecting'); setTranscript([]); setInterim(''); endedRef.current = false;
    setDesign(null); setHero(null); setVariants({}); setWire(null);

    // Unlock <audio> + speechSynthesis INSIDE the tap so playback works after async fetch.
    try { const a = new Audio(); audioRef.current = a; a.muted = true; const p = a.play(); if (p && p.then) p.then(() => { a.pause(); a.currentTime = 0; a.muted = false; }).catch(() => {}); } catch (e) {}
    try { window.speechSynthesis.cancel(); window.speechSynthesis.resume(); } catch (e) {}

    histRef.current = [];
    const greet = 'Hi, I’m Eve, your designer at Studio 76. Tell me about a piece you’ve always wanted.';
    push('eve', greet); histRef.current.push({ role: 'eve', text: greet });
    speak(greet);          // ElevenLabs voice (browser fallback) — gesture already unlocked audio
    setV('live');

    try { await navigator.mediaDevices.getUserMedia({ audio: true }); } catch (e) {
      setErr('Microphone access is needed to talk to Eve — or just describe your piece below.');
      endedRef.current = true; try { window.speechSynthesis.cancel(); } catch (e2) {}
      setV('error'); setMode('console'); return;
    }
    runConversation();
  }
  function endVoice() {
    endedRef.current = true; speakingRef.current = false;
    try { if (recogRef.current) recogRef.current.stop(); } catch (e) {}
    try { window.speechSynthesis.cancel(); } catch (e) {}
    try { if (audioRef.current) { audioRef.current.pause(); audioRef.current.src = ''; } } catch (e) {}
    setV('ended'); setSpeaking(false); setListening(false); setInterim('');
  }

  /* ---------- typed ---------- */
  async function imagine(text) {
    const d = (text != null ? text : dream).trim();
    if (d.length < 4) { setErr('Tell us a little more about the piece you want.'); return; }
    setErr(''); setDream(d); setPhase('thinking');
    setDesign(null); setHero(null); setVariants({}); setWire(null); setActive('original'); setView('render');
    try {
      const b = await imagApi('brief', { dream: d });
      setDesign({ concept: b.concept, spec: b.spec }); setPhase('result');
      const id = await renderHero(b.spec);
      renderExtras(id);
    } catch (e) { setErr(e.message || 'Could not imagine that — try again.'); setPhase('idle'); }
  }

  /* ---------- derived ---------- */
  const isUrl = (x) => typeof x === 'string' && x !== 'loading';
  const mainSrc = view === 'blueprint'
    ? (isUrl(wire) ? wire : null)
    : (isUrl(variants[active]) ? variants[active] : (hero && hero.dataUrl));
  const concept = design && design.concept;
  const spec = design && design.spec;
  const hasStage = phase === 'thinking' || phase === 'result' || !!design || !!hero || (mode === 'voice' && (vstate === 'live' || vstate === 'ended'));
  const showRail = !hasStage && mode === 'console';
  const stageMsg = !design ? (mode === 'voice' ? 'Tell Eve what you’ve always wanted…' : 'Reading your description…') : (!hero ? 'Designing your piece…' : (view === 'blueprint' ? 'Drafting the blueprint…' : 'Rendering in solid gold…'));

  return (
    <section id="imaginarium" className="cream section-pad imag">
      <div className="wrap">
        <div className="imag-head reveal">
          <Eyebrow>The Imaginarium</Eyebrow>
          <h2 className="section-title" style={{marginTop:14}}>
            Talk to <span className="serif-italic" style={{color:'var(--gold-light)'}}>your designer.</span>
          </h2>
          <p className="imag-sub">
            Speak it to Eve, our designer, or describe it — and watch your piece render in gold, in seconds.
            New-age, fine, made for you.
          </p>
        </div>

        {/* ---- CONSOLE ---- */}
        {mode === 'console' && (
          <div className="imag-console reveal reveal-d1">
            <button className="imag-voicecta" onClick={startVoice}>
              <span className="imag-orb sm"><span className="imag-orb-ring"></span>
                <span className="imag-mic" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" width="22" height="22"><path d="M12 14.5a2.8 2.8 0 0 0 2.8-2.8V6.8a2.8 2.8 0 1 0-5.6 0v4.9A2.8 2.8 0 0 0 12 14.5Z" stroke="currentColor" strokeWidth="1.6"/><path d="M6.5 11.3a5.5 5.5 0 0 0 11 0M12 17.3V21" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg></span>
              </span>
              <span className="imag-voicecta-t"><b>Talk to Eve, your designer</b><span>{voiceReady ? 'Tap, allow your mic, and just speak — she designs it with you' : 'Voice needs a supported browser — for now, describe your piece below'}</span></span>
              <span className="imag-voicecta-go"><ArrowR style={{width:20,height:20}}/></span>
            </button>
            <div className="imag-or"><span>or describe it</span></div>
            <div className="imag-form">
              <textarea className="imag-textarea" ref={taRef} rows={3} maxLength={1200}
                placeholder="A piece you saw once and never forgot… the stone, the shape, the feeling."
                value={dream} onChange={e => setDream(e.target.value)}
                onKeyDown={e => { if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) imagine(); }} />
              <div className="imag-form-foot">
                <div className="imag-examples">
                  {IMAG_EXAMPLES.map((ex, i) => <button key={i} className="imag-ex" onClick={() => { setDream(ex); imagine(ex); }}>{ex}</button>)}
                </div>
                <button className="btn btn-primary imag-go" onClick={() => imagine()} disabled={phase === 'thinking'}>
                  {phase === 'thinking' ? 'Imagining…' : 'Imagine it'} <ArrowR style={{width:18,height:18}}/>
                </button>
              </div>
            </div>
            {err && <p className="imag-err">{err}</p>}

            {showRail && (
              <div className="imag-rail">
                <span className="imag-rail-k"><i className="spark">✦</i> Fine · new-age · made for you</span>
                <div className="imag-group">
                  {[ASSETS.ringSnake, ASSETS.polki, ASSETS.cuffWing].map((img, i) => (
                    <div className="imag-group-img frame" key={i}><img className="img-hover" src={img} alt="" loading="lazy" /></div>
                  ))}
                </div>
              </div>
            )}
          </div>
        )}

        {/* ---- VOICE BAR ---- */}
        {mode === 'voice' && (
          <div className="imag-voicebar reveal">
            <div className="imag-voice-left">
              <div className={'imag-orb' + (speaking ? ' speaking' : (listening ? ' listening' : '')) + (vstate === 'connecting' ? ' connecting' : '')}>
                <span className="imag-orb-ring"></span><span className="imag-orb-ring r2"></span><span className="imag-orb-core"></span>
              </div>
              <div className="imag-voice-status">
                <span className="imag-voice-name">Eve · your designer</span>
                <span className="imag-voice-state">{vstate === 'connecting' ? 'Starting…' : vstate === 'live' ? (speaking ? 'Speaking…' : (listening ? 'Listening…' : 'One moment…')) : vstate === 'ended' ? 'Conversation ended' : 'Tap to talk again'}</span>
              </div>
            </div>
            <div className="imag-transcript" ref={tEndRef}>
              {transcript.length === 0 && vstate !== 'ended' && <p className="imag-tc-hint">Say hello, and tell Eve about a piece you’ve always wanted…</p>}
              {transcript.slice(-8).map((m, i) => <p key={i} className={'imag-tc ' + m.role}><span className="imag-tc-who">{m.role === 'you' ? 'You' : 'Eve'}</span>{m.text}</p>)}
              {interim && <p className="imag-tc you" style={{opacity:.55}}><span className="imag-tc-who">You</span>{interim}…</p>}
            </div>
            <div className="imag-voice-ctrls">
              {(vstate === 'live' || vstate === 'connecting') && <button className="btn btn-light imag-end" onClick={endVoice}>End</button>}
              {(vstate === 'ended' || vstate === 'error') && <button className="btn btn-primary" onClick={startVoice}>Talk again</button>}
              <button className="imag-link" onClick={() => { endVoice(); setMode('console'); }}>Type instead</button>
            </div>
            {err && <p className="imag-err" style={{flexBasis:'100%'}}>{err}</p>}
          </div>
        )}

        {/* ---- SHARED RESULT STAGE ---- */}
        {hasStage && (
          <div className="imag-stage">
            <div className="imag-viewer reveal">
              <div className="imag-frame frame r-lg">
                {mainSrc && !(view === 'blueprint' && wire === 'loading')
                  ? <img src={mainSrc} alt={concept ? concept.name : 'Your piece'} />
                  : <div className="imag-skel"><span className="imag-spin"></span><span className="imag-skel-txt">{stageMsg}</span></div>}
                {hero && view === 'render' && <span className="imag-badge">✦ Nano Banana 2</span>}
                {hero && view === 'blueprint' && <span className="imag-badge">Technical blueprint</span>}
              </div>
              {hero && (
                <div className="imag-controls">
                  <div className="imag-toggle">
                    <button className={view === 'render' ? 'on' : ''} onClick={() => setView('render')}>Render</button>
                    <button className={view === 'blueprint' ? 'on' : ''} onClick={() => setView('blueprint')} disabled={!wire}>{wire === 'loading' ? 'Blueprint…' : 'Blueprint'}</button>
                  </div>
                  <div className="imag-thumbs">
                    {IMAG_TONES.map(t => {
                      const val = variants[t.k]; const isImg = isUrl(val);
                      return (
                        <button key={t.k} className={'imag-thumb' + (active === t.k && view === 'render' ? ' on' : '')} disabled={!isImg} title={t.label} onClick={() => { setActive(t.k); setView('render'); }}>
                          {isImg ? <img src={val} alt={t.label} /> : val === 'loading' ? <span className="imag-spin sm"></span> : <span className="imag-thumb-x">+</span>}
                          <span className="imag-thumb-l">{t.label}</span>
                        </button>
                      );
                    })}
                  </div>
                  <div className="imag-actions">
                    {mainSrc && <a className="link-arrow" href={mainSrc} download={(concept && concept.name ? concept.name.replace(/\s+/g,'-') : 'studio76-piece') + '.png'}>Download</a>}
                    <button className="btn btn-light" onClick={onBegin}>Bring this to the studio <ArrowR style={{width:16,height:16}}/></button>
                  </div>
                </div>
              )}
            </div>

            <div className="imag-side reveal reveal-d1">
              {concept ? (
                <div className="imag-concept">
                  <span className="imag-kicker"><i className="spark">✦</i> The best potential piece</span>
                  <h3 className="imag-name serif">{concept.name}</h3>
                  {concept.description && <p className="imag-desc">{concept.description}</p>}
                  {concept.why_it_works && <p className="imag-why"><span className="imag-why-k">Why it works</span>{concept.why_it_works}</p>}
                  {(concept.price_tier || concept.indicative_range) && (
                    <div className="imag-price">
                      {concept.price_tier && <span className="pill">{concept.price_tier}</span>}
                      {concept.indicative_range && <span className="imag-range">{concept.indicative_range}</span>}
                    </div>
                  )}
                </div>
              ) : (
                <div className="imag-concept imag-concept-skel"><span className="imag-spin"></span><p className="imag-skel-txt">{mode === 'voice' ? 'Eve is listening…' : 'Composing your concept…'}</p></div>
              )}
              {spec && (spec.piece_type || spec.silhouette) && (
                <div className="imag-spec">
                  <span className="imag-kicker"><i className="spark">✦</i> We broke it down</span>
                  <SpecRow label="Piece" value={spec.piece_type} />
                  <SpecRow label="Silhouette" value={spec.silhouette} />
                  <SpecRow label="Hero stone" value={fmtStone(spec.hero_stone)} />
                  <SpecRow label="Accents" value={fmtAccents(spec.accent_stones)} />
                  <SpecRow label="Metal" value={spec.metal_tone} />
                  <SpecRow label="Motif" value={spec.motif} />
                  <SpecRow label="Era / mood" value={spec.era_mood} />
                  <SpecRow label="Setting" value={spec.setting_style} />
                </div>
              )}
            </div>
          </div>
        )}
      </div>
    </section>
  );
}

Object.assign(window, { Imaginarium });
