// Studio editor · document pane: kicker/headline/dek/byline, HTML body + toolbar,
// hero image (upload / generate / focal / caption), AI assists, em-dash guard.

function deDash(s){ return String(s||'').replace(/\s* · \s*/g, ', '); }
window.deDash = deDash;

function bodyWords(html){
  const d = document.createElement('div'); d.innerHTML = html||'';
  const t = (d.textContent||'').trim();
  return t ? t.split(/\s+/).length : 0;
}
window.bodyWords = bodyWords;

function slugify(s){ return String(s||'').toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,''); }
window.slugify = slugify;

// Searchable combobox · pick from a registry, type to filter, add inline
function Combo({value, onChange, options, onAdd, addLabel, placeholder}){
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  const wrapRef = React.useRef(null);
  React.useEffect(()=>{
    const f = e=>{ if(wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    window.addEventListener('mousedown', f);
    return ()=>window.removeEventListener('mousedown', f);
  }, []);
  const list = options.filter(o=>!q.trim() || o.toLowerCase().includes(q.trim().toLowerCase()));
  const canAdd = onAdd && q.trim() && !options.some(o=>o.toLowerCase()===q.trim().toLowerCase());
  return (
    <div ref={wrapRef} style={{position:'relative'}}>
      <input value={open?q:value} placeholder={placeholder||value}
        onFocus={()=>{setOpen(true);setQ('');}}
        onChange={e=>setQ(e.target.value)}
        style={{width:'100%',padding:'8px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',fontSize:13,outline:'none',fontFamily:'var(--sans)'}}/>
      <span className="mono" style={{position:'absolute',right:9,top:'50%',transform:'translateY(-50%)',fontSize:8,color:'var(--mute)',pointerEvents:'none'}}>▾</span>
      {open && (
        <div style={{position:'absolute',zIndex:30,left:0,right:0,top:'100%',maxHeight:190,overflowY:'auto',background:'var(--bg)',border:'1px solid var(--rule-line)',borderTop:0,boxShadow:'0 10px 22px rgba(11,11,12,.1)'}}>
          {list.map(o=>(
            <button key={o} onMouseDown={e=>{e.preventDefault();onChange(o);setOpen(false);}}
              style={{display:'block',width:'100%',textAlign:'left',padding:'7px 10px',border:0,borderBottom:'1px solid var(--rule-line)',background:o===value?'var(--canvas)':'transparent',cursor:'pointer',fontSize:12.5,fontFamily:'var(--sans)'}}>{o}</button>
          ))}
          {!list.length && !canAdd && <div className="mono" style={{padding:'8px 10px',fontSize:9.5,letterSpacing:'.08em',color:'var(--mute)'}}>NO MATCHES</div>}
          {canAdd && (
            <button onMouseDown={e=>{e.preventDefault();onAdd(q.trim());onChange(q.trim());setOpen(false);}}
              style={{display:'block',width:'100%',textAlign:'left',padding:'8px 10px',border:0,background:'transparent',cursor:'pointer',fontSize:12,color:'var(--red)',fontWeight:600,fontFamily:'var(--sans)'}}>+ {addLabel||'Add'} "{q.trim()}"</button>
          )}
        </div>
      )}
    </div>
  );
}
window.Combo = Combo;

// abstract generated-image placeholder (simulates the AI image service)
function genHeroSvg(headline, desk){
  const palettes = { Business:['#0A3161','#3D6EA5'], Politics:['#B31942','#0A3161'], Sports:['#1f8a5b','#0A3161'], Civic:['#3D6EA5','#B31942'] };
  const [a,b] = palettes[desk] || ['#0A3161','#B31942'];
  const seed = (headline||'x').length;
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="900"><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="${a}"/><stop offset="1" stop-color="${b}"/></linearGradient></defs><rect width="1600" height="900" fill="url(#g)"/><circle cx="${300+seed*17%900}" cy="${200+seed*31%500}" r="260" fill="#ffffff" opacity=".08"/><circle cx="${1100-seed*13%600}" cy="${600-seed*7%300}" r="380" fill="#ffffff" opacity=".06"/><rect x="0" y="770" width="1600" height="130" fill="#000000" opacity=".18"/><text x="60" y="850" font-family="Georgia,serif" font-size="52" fill="#ffffff" opacity=".9">TUEPAC · ${desk||'Desk'}</text></svg>`;
  return 'data:image/svg+xml;utf8,'+encodeURIComponent(svg);
}

function HeroImage({img, setImg, focal, setFocal, caption, setCaption, headline, desk}){
  const [status, setStatus] = React.useState('');
  const boxRef = React.useRef(null);
  const dragRef = React.useRef(false);
  const fileRef = React.useRef(null);

  function onFile(e){
    const f = e.target.files && e.target.files[0];
    if(!f) return;
    setStatus('optimizing…');
    const r = new FileReader();
    r.onload = ()=>{ setImg({src:r.result, kind:'upload'}); setFocal({x:50,y:50}); setStatus('uploaded ✓ · drag the preview to set focus'); };
    r.onerror = ()=>setStatus('upload failed · try a smaller file');
    r.readAsDataURL(f);
    e.target.value = '';
  }
  function generate(){
    setStatus('generating…');
    setTimeout(()=>{
      setImg({src:genHeroSvg(headline, desk), kind:'gen'});
      setFocal({x:50,y:50});
      setStatus('generated ✓ · drag the preview to set focus');
    }, 700);
  }
  function setFromEvent(e){
    const r = boxRef.current.getBoundingClientRect();
    const x = Math.round(Math.min(100, Math.max(0, ((e.clientX-r.left)/r.width)*100)));
    const y = Math.round(Math.min(100, Math.max(0, ((e.clientY-r.top)/r.height)*100)));
    setFocal({x,y});
  }

  return (
    <div style={{marginBottom:14}}>
      <div className="eyebrow" style={{marginBottom:6}}>HERO IMAGE <span style={{color:'var(--mute)',textTransform:'none',letterSpacing:'.04em'}}>· 16:9 · every published piece carries one</span></div>
      {!img && (
        <div style={{border:'1px dashed var(--rule-line)',background:'var(--canvas)',padding:'22px 16px',display:'flex',gap:10,alignItems:'center',justifyContent:'center',flexWrap:'wrap'}}>
          <button className="btn k s" onClick={()=>fileRef.current&&fileRef.current.click()}>✦ Add image</button>
          <button className="btn gh s" onClick={generate}>✦ Generate image</button>
          <span className="mono" style={{fontSize:9.5,color:'var(--mute)',letterSpacing:'.08em'}}>UPLOAD IS OPTIMIZED TO ≤1600PX · GENERATE USES HEADLINE + DEK</span>
        </div>
      )}
      {img && (
        <div>
          <div ref={boxRef} style={{position:'relative',aspectRatio:'16/9',overflow:'hidden',background:'var(--canvas)',cursor:'crosshair',touchAction:'none'}}
            onPointerDown={e=>{dragRef.current=true;boxRef.current.setPointerCapture(e.pointerId);setFromEvent(e);}}
            onPointerMove={e=>{if(dragRef.current)setFromEvent(e);}}
            onPointerUp={()=>{dragRef.current=false;}}>
            <img src={img.src} alt="" draggable="false" style={{width:'100%',height:'100%',objectFit:'cover',objectPosition:`${focal.x}% ${focal.y}%`,display:'block',userSelect:'none'}}/>
            <span style={{position:'absolute',left:`${focal.x}%`,top:`${focal.y}%`,width:22,height:22,margin:'-11px 0 0 -11px',border:'2px solid #fff',borderRadius:'50%',boxShadow:'0 0 0 1px rgba(0,0,0,.5), inset 0 0 0 1px rgba(0,0,0,.5)',pointerEvents:'none'}}></span>
            <span className="mono" style={{position:'absolute',right:8,bottom:8,fontSize:9,letterSpacing:'.1em',background:'rgba(0,0,0,.55)',color:'#fff',padding:'3px 8px'}}>FOCAL {focal.x}% {focal.y}%</span>
          </div>
          <div style={{display:'flex',gap:8,alignItems:'center',marginTop:8,flexWrap:'wrap'}}>
            <button className="btn gh s" onClick={()=>fileRef.current&&fileRef.current.click()}>Replace image</button>
            <button className="btn gh s" onClick={generate}>↻ Generate again</button>
            <button className="btn gh s" onClick={()=>{setImg(null);setCaption('');setStatus('');}}>Remove</button>
            <span className="mono" style={{fontSize:9.5,color:'var(--mute)',letterSpacing:'.06em'}}>{img.kind==='gen'?'AI-GENERATED':'UPLOADED'}</span>
          </div>
          <input value={caption} onChange={e=>setCaption(deDash(e.target.value))} placeholder="Caption (optional) · who/what/where"
            style={{width:'100%',marginTop:8,padding:'7px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',fontSize:12,outline:'none',fontFamily:'var(--sans)'}}/>
        </div>
      )}
      <input ref={fileRef} type="file" accept="image/*" onChange={onFile} style={{display:'none'}}/>
      {status && <div className="mono" style={{fontSize:9.5,color:status.includes('failed')?'var(--red)':'var(--mute)',letterSpacing:'.08em',marginTop:6}}>{status.toUpperCase()}</div>}
    </div>
  );
}

function BodyToolbar({onCmd}){
  const btns = [['bold','B'],['italic','I'],['h2','H2'],['link','Link'],['unlink','Unlink'],['video','▶ Video']];
  return (
    <div style={{display:'flex',gap:4,marginBottom:6}}>
      {btns.map(([k,lbl])=>(
        <button key={k} className="btn gh s" style={{fontWeight:k==='bold'?800:500,fontStyle:k==='italic'?'italic':'normal'}}
          onMouseDown={e=>{e.preventDefault();onCmd(k);}}>{lbl}</button>
      ))}
      <span className="mono" style={{fontSize:9,color:'var(--mute)',letterSpacing:'.08em',alignSelf:'center',marginLeft:6}}>SAVES AS FORMATTED HTML</span>
    </div>
  );
}

function scriptedHeadlines(h, desk, city){
  const base = (h||'Untitled').replace(/[.:]$/,'');
  const lc = base.charAt(0).toLowerCase()+base.slice(1);
  const loc = String(city||'').trim();
  return [
    base,
    `${base}: what it changes`,
    `${loc?loc+' watch: ':'Watch: '}${lc}`,
    `The ${desk.toLowerCase()} story behind ${lc}`,
    `${base}, by the numbers`,
  ].map(s=>s.length>90?s.slice(0,90):s);
}

function scriptedTighten(html){
  const d = document.createElement('div'); d.innerHTML = html;
  d.querySelectorAll('p').forEach(p=>{
    const parts = p.textContent.split(/(?<=[.!?])\s+/);
    if(parts.length>2) p.textContent = parts.slice(0, parts.length-1).join(' ');
  });
  return d.innerHTML;
}

function DraftEditor(p){
  const bodyRef = React.useRef(null);
  const [hls, setHls] = React.useState(null);
  const [busy, setBusy] = React.useState('');
  const [dashHint, setDashHint] = React.useState(false);

  React.useEffect(()=>{ if(bodyRef.current && bodyRef.current.innerHTML!==p.body) bodyRef.current.innerHTML = p.body; }, []);

  function syncBody(){
    if(!bodyRef.current) return;
    let html = bodyRef.current.innerHTML;
    if(html.includes(' · ')){
      html = deDash(html);
      bodyRef.current.innerHTML = html;
      setDashHint(true); setTimeout(()=>setDashHint(false), 3500);
    }
    p.setBody(html);
  }
  function cmd(k){
    if(k==='h2') document.execCommand('formatBlock', false, 'h2');
    else if(k==='video'){
      const u = prompt('YouTube link (watch, shorts, or youtu.be)');
      if(u){
        const id = window.ytId ? window.ytId(u) : null;
        if(!id){ alert('No video ID found in that link.'); return; }
        const cap = prompt('Caption (optional)') || '';
        document.execCommand('insertHTML', false,
          '<figure class="yt-embed" data-yt="'+id+'" contenteditable="false"><img src="https://i.ytimg.com/vi/'+id+'/hqdefault.jpg" alt=""><figcaption>'+(cap?deDash(cap)+' · ':'')+'Video plays on the published page</figcaption></figure><p><br></p>');
      }
    }
    else if(k==='link'){ const u = prompt('Link URL'); if(u) document.execCommand('createLink', false, u); }
    else if(k==='unlink') document.execCommand('unlink');
    else document.execCommand(k);
    syncBody();
  }
  async function suggest(){
    setBusy('headlines');
    let list = null;
    if(p.llmOn && window.claude && window.claude.complete){
      try{
        const raw = await window.claude.complete(`Suggest 5 alternate headlines (each under 13 words, specific, no clickbait, no em dashes) for this piece. Return ONLY a JSON array of strings.\nHEADLINE: ${p.headline}\nDEK: ${p.dek}`);
        const c = String(raw).replace(/```json|```/g,'').trim();
        list = JSON.parse(c.slice(c.indexOf('['), c.lastIndexOf(']')+1));
      }catch(e){ list = null; }
    }
    setHls((list||scriptedHeadlines(p.headline, p.desk, p.city)).map(deDash));
    setBusy('');
  }
  async function tighten(){
    setBusy('tighten');
    let out = null;
    if(p.llmOn && window.claude && window.claude.complete){
      try{
        const raw = await window.claude.complete(`Tighten this article body by 15-20%. Keep every fact, keep the paragraph structure, no em dashes. Input is HTML; return ONLY the tightened HTML.\n${p.body}`);
        out = String(raw).replace(/```html|```/g,'').trim();
        if(!out.includes('<p')) out = null;
      }catch(e){ out = null; }
    }
    const html = deDash(out || scriptedTighten(p.body));
    p.setBody(html);
    if(bodyRef.current) bodyRef.current.innerHTML = html;
    setBusy('');
  }

  return (
    <div className="lay2" style={{display:'grid',gridTemplateColumns:'1fr 280px',gap:18,alignItems:'flex-start'}}>
      <Card title="Document" meta={`${p.words} words · every field editable`}
        actions={<>
          <button className="btn gh" onClick={p.onSaveDraft}>Save draft</button>
          <button className="btn gh" onClick={p.onRegen}>↻ Regenerate</button>
          <button className="btn k" onClick={p.onNext}>Review & publish →</button>
        </>}>
        {(p.genNote||p.genErr) && (
          <div style={{padding:'7px 10px',marginBottom:12,fontSize:10.5,lineHeight:1.45,
            background:p.genErr?'rgba(179,25,66,.1)':'rgba(31,46,79,.05)',border:'1px solid '+(p.genErr?'rgba(154,21,55,.32)':'rgba(31,46,79,.16)'),color:p.genErr?'var(--gold-2)':'var(--navy)'}}>
            <span className="mono" style={{fontSize:9,letterSpacing:'.12em'}}>{p.genErr?'⚠ FALLBACK':'✦ AI DRAFT'}</span> · {p.genErr||p.genNote}
          </div>
        )}

        <HeroImage img={p.img} setImg={p.setImg} focal={p.focal} setFocal={p.setFocal} caption={p.caption} setCaption={p.setCaption} headline={p.headline} desk={p.desk}/>

        <div style={{display:'grid',gridTemplateColumns:'160px 1fr',gap:14}}>
          <div>
            <div className="eyebrow" style={{marginBottom:5}}>KICKER</div>
            <input value={p.kicker} onChange={e=>p.setKicker(deDash(e.target.value))} placeholder="e.g. EXCLUSIVE" maxLength={24}
              style={{width:'100%',padding:'8px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',fontSize:11,fontFamily:'var(--mono)',letterSpacing:'.1em',textTransform:'uppercase',outline:'none'}}/>
          </div>
          <div>
            <div className="eyebrow" style={{marginBottom:5}}>BYLINE · AUTHOR</div>
            <Combo value={p.byline} onChange={v=>p.setByline(v)}
              options={(window.AUTHORS||[]).map(a=>a.name)}
              onAdd={n=>window.addAuthor && window.addAuthor(n)}
              addLabel="Add author"/>
          </div>
        </div>

        <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',margin:'12px 0 5px'}}>
          <div className="eyebrow">HEADLINE</div>
          <button className="btn gh s" onClick={suggest} disabled={busy==='headlines'}>{busy==='headlines'?'thinking…':'✦ Suggest headlines'}</button>
        </div>
        <textarea value={p.headline} onChange={e=>p.setHeadline(deDash(e.target.value))} className="serif"
          style={{width:'100%',padding:'8px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',outline:'none',resize:'vertical',display:'block',minHeight:54,fontSize:24,fontWeight:700,lineHeight:1.1,letterSpacing:'-.01em'}}/>
        {hls && (
          <div style={{border:'1px solid var(--rule-line)',borderTop:0,background:'var(--canvas)'}}>
            {hls.map((h,i)=>(
              <button key={i} onClick={()=>{p.setHeadline(h);setHls(null);}}
                style={{display:'block',width:'100%',textAlign:'left',padding:'7px 10px',border:0,borderBottom:i<hls.length-1?'1px solid var(--rule-line)':'0',background:'transparent',cursor:'pointer',fontFamily:'var(--serif)',fontSize:13.5,fontWeight:600}}>{h}</button>
            ))}
            <button onClick={()=>setHls(null)} className="mono" style={{display:'block',width:'100%',padding:'5px 10px',border:0,background:'transparent',cursor:'pointer',fontSize:9,letterSpacing:'.12em',color:'var(--mute)'}}>DISMISS</button>
          </div>
        )}

        <div className="eyebrow" style={{margin:'12px 0 5px'}}>DEK</div>
        <textarea value={p.dek} onChange={e=>p.setDek(deDash(e.target.value))}
          style={{width:'100%',padding:'8px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',outline:'none',resize:'vertical',display:'block',minHeight:50,fontFamily:'var(--serif)',fontSize:15}}/>

        <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',margin:'12px 0 5px'}}>
          <div className="eyebrow">BODY</div>
          <button className="btn gh s" onClick={tighten} disabled={busy==='tighten'}>{busy==='tighten'?'tightening…':'✦ Tighten the draft'}</button>
        </div>
        <BodyToolbar onCmd={cmd}/>
        <div ref={bodyRef} contentEditable suppressContentEditableWarning onInput={syncBody} onBlur={syncBody}
          className="studio-body" style={{width:'100%',minHeight:320,padding:'12px 14px',border:'1px solid var(--rule-line)',background:'var(--bg)',outline:'none',fontFamily:'IBM Plex Serif, Georgia, serif',fontSize:14.5,lineHeight:1.65}}></div>
        {dashHint && <div className="mono" style={{fontSize:9.5,color:'var(--gold-2)',letterSpacing:'.08em',marginTop:5}}>EM DASH AUTO-CORRECTED · HOUSE STYLE USES COMMAS AND COLONS</div>}
        {p.savedNote && <div className="mono" style={{fontSize:9.5,color:'var(--green)',letterSpacing:'.08em',marginTop:8}}>✓ DRAFT SAVED TO ORIGINALS</div>}
      </Card>

      <div style={{display:'flex',flexDirection:'column',gap:14}}>
        <Card title="Rights & disclosure">
          <Seg value={p.rights} onChange={p.setRights} options={['Original','Analysis','Sponsored']}/>
          {p.rights==='Sponsored' && (
            <input value={p.sponsor} onChange={e=>p.setSponsor(e.target.value)} placeholder="Sponsor name" style={{width:'100%',marginTop:10,padding:'8px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',fontSize:13,outline:'none',fontFamily:'var(--sans)'}}/>
          )}
          <div style={{marginTop:10,fontSize:11,color:'var(--ink-2)',lineHeight:1.5}}>
            {p.rights==='Sponsored' ? 'Runs with a clear sponsored-content disclosure label.'
             : p.rights==='Analysis' ? 'Labeled analysis: interpretation on the record, held to the same sourcing standard.'
             : 'TUEPAC-commissioned. Carries a TUEPAC Editorial or named-author byline and canonical on tuepac.com.'}
          </div>
        </Card>

        <Card title="Attribution">
          <div style={{fontSize:11,color:'var(--mute)',marginBottom:8,lineHeight:1.4}}>Who wrote it, where it runs, and the topic it files under.</div>
          <div className="eyebrow" style={{marginBottom:5}}>PUBLICATION</div>
          <Combo value={p.pubName} onChange={p.setPubName}
            options={[...(window.PUBLICATIONS||[]).map(x=>x.name), ...(window.PUBLISHERS||[]).map(x=>x.name)]}
            onAdd={n=>window.addPublication && window.addPublication(n)}
            addLabel="Add publication"/>
          <div className="eyebrow" style={{margin:'12px 0 5px'}}>TOPIC</div>
          <select value={p.desk} onChange={e=>p.setDesk(e.target.value)}
            style={{width:'100%',padding:'8px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',fontSize:13,outline:'none',fontFamily:'var(--sans)'}}>
            {(window.TAGS||[p.desk]).map(t=><option key={t}>{t}</option>)}
          </select>
          <div className="mono" style={{fontSize:9,letterSpacing:'.08em',color:'var(--mute)',marginTop:8}}>PUBLISHES TO THE MATCHING TOPIC PAGE ON THE PUBLIC SITE</div>
        </Card>

        <Card title="SEO">
          <div className="eyebrow" style={{marginBottom:5}}>SLUG</div>
          <div style={{display:'flex',alignItems:'center',border:'1px solid var(--rule-line)',background:'var(--canvas)'}}>
            <span className="mono" style={{fontSize:10,color:'var(--mute)',padding:'0 0 0 8px'}}>/{slugify(p.desk)}/</span>
            <input value={p.slug} onChange={e=>p.setSlug(e.target.value)} style={{flex:1,border:0,background:'transparent',padding:'7px 8px',fontSize:12,outline:'none',fontFamily:'var(--mono)'}}/>
          </div>
          <div className="eyebrow" style={{margin:'12px 0 5px'}}>META DESCRIPTION</div>
          <textarea value={p.meta} onChange={e=>p.setMeta(deDash(e.target.value))} maxLength={180} style={{width:'100%',padding:'8px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',fontSize:12,outline:'none',resize:'vertical',display:'block',minHeight:56,fontFamily:'var(--sans)'}}/>
          <div className="mono" style={{fontSize:9,color:'var(--mute)',textAlign:'right',marginTop:3}}>{p.meta.length}/155</div>
        </Card>
      </div>
    </div>
  );
}

Object.assign(window, { DraftEditor, HeroImage, deDash, Combo, slugify });
