// Fluentra Console v2 — all pages with real API data.

/* ── Shortcuts ─────────────────────────────────────────────── */
const apiFetch = (...args) => window.__FETCH__(...args);
const useAuth  = ()        => window.useAuth();

/* ══════════════════════════════════════════════════════════════
   ICON COMPONENT
   ══════════════════════════════════════════════════════════ */
const I = ({ name, size = 16 }) => {
  const c = { width:size,height:size,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:1.6,strokeLinecap:'round',strokeLinejoin:'round' };
  switch (name) {
    case 'grid':     return <svg {...c}><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>;
    case 'diagram':  return <svg {...c}><rect x="3" y="3" width="6" height="6" rx="1"/><rect x="15" y="15" width="6" height="6" rx="1"/><rect x="15" y="3" width="6" height="6" rx="1"/><path d="M9 6h6M18 9v6M9 6a4 4 0 0 0 4 4h2"/></svg>;
    case 'play':     return <svg {...c}><path d="M7 5v14l12-7L7 5Z"/></svg>;
    case 'history':  return <svg {...c}><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l3 2"/></svg>;
    case 'edit':     return <svg {...c}><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>;
    case 'settings': return <svg {...c}><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z"/></svg>;
    case 'bell':     return <svg {...c}><path d="M6 8a6 6 0 1 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10 21a2 2 0 0 0 4 0"/></svg>;
    case 'refresh':  return <svg {...c}><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 21v-5h5"/></svg>;
    case 'search':   return <svg {...c}><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>;
    case 'plus':     return <svg {...c}><path d="M12 5v14M5 12h14"/></svg>;
    case 'trash':    return <svg {...c}><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M6 6l1 14a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2l1-14"/></svg>;
    case 'eye':      return <svg {...c}><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>;
    case 'pencil':   return <svg {...c}><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>;
    case 'copy':     return <svg {...c}><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>;
    case 'upload':   return <svg {...c}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5-5 5 5"/><path d="M12 5v12"/></svg>;
    case 'download': return <svg {...c}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></svg>;
    case 'save':     return <svg {...c}><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2Z"/><path d="M17 21v-8H7v8M7 3v5h8"/></svg>;
    case 'pause':    return <svg {...c}><rect x="6" y="4" width="4" height="16" rx="1"/><rect x="14" y="4" width="4" height="16" rx="1"/></svg>;
    case 'stop':     return <svg {...c}><rect x="5" y="5" width="14" height="14" rx="2"/></svg>;
    case 'bolt':     return <svg {...c}><path d="M13 2 4 14h7l-1 8 9-12h-7l1-8Z"/></svg>;
    case 'check':    return <svg {...c}><path d="M20 6 9 17l-5-5"/></svg>;
    case 'x':        return <svg {...c}><path d="m18 6-12 12M6 6l12 12"/></svg>;
    case 'chevron':  return <svg {...c}><path d="m6 9 6 6 6-6"/></svg>;
    case 'warn':     return <svg {...c}><path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/><path d="M12 9v4M12 17h.01"/></svg>;
    case 'link':     return <svg {...c}><path d="M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1"/><path d="M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1"/></svg>;
    case 'user':     return <svg {...c}><circle cx="12" cy="8" r="4"/><path d="M4 20a8 8 0 0 1 16 0"/></svg>;
    case 'users':    return <svg {...c}><circle cx="9" cy="8" r="4"/><path d="M3 20a6 6 0 0 1 12 0"/><path d="M16 4a4 4 0 0 1 0 8M21 20a6 6 0 0 0-4-5.7"/></svg>;
    case 'card':     return <svg {...c}><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 10h18"/></svg>;
    case 'filter':   return <svg {...c}><path d="M3 4h18l-7 9v6l-4-2v-4Z"/></svg>;
    case 'menu':     return <svg {...c}><path d="M3 6h18M3 12h18M3 18h18"/></svg>;
    case 'lock':     return <svg {...c}><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>;
    case 'info':     return <svg {...c}><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>;
    case 'mail':     return <svg {...c}><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>;
    case 'logout':   return <svg {...c}><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>;
  }
  return null;
};
window.I = I;

const Badge = ({ kind='neutral', children, dot=true }) => (
  <span className={`badge b-${kind}`}>{dot && <span className="b-dot"/>}{children}</span>
);
window.Badge = Badge;

const PageHead = ({ title, sub, actions }) => (
  <div className="page-head">
    <div><h1 className="page-title">{title}</h1>{sub && <p className="page-sub">{sub}</p>}</div>
    {actions && <div className="page-actions">{actions}</div>}
  </div>
);
window.PageHead = PageHead;

/* ── Custom Select (replaces native <select> for full styling control) ── */
function CustomSelect({ value, onChange, options, placeholder='', className='', searchable=false }) {
  const [open, setOpen] = React.useState(false);
  const [query, setQuery] = React.useState('');
  const ref = React.useRef(null);
  const inputRef = React.useRef(null);
  const selected = options.find(o => o.value === value);
  const filtered = searchable && query.trim()
    ? options.filter(o => o.label.toLowerCase().includes(query.toLowerCase()))
    : options;

  React.useEffect(() => {
    const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, []);

  React.useEffect(() => {
    if (open && searchable && inputRef.current) {
      setQuery('');
      setTimeout(() => inputRef.current?.focus(), 10);
    }
  }, [open, searchable]);

  return (
    <div ref={ref} style={{position:'relative'}}>
      <div
        className={'input mono ' + className}
        onClick={()=>setOpen(v=>!v)}
        style={{cursor:'pointer',display:'flex',alignItems:'center',justifyContent:'space-between',userSelect:'none'}}
      >
        <span style={{color:selected?'var(--ink)':'var(--ink-faint)'}}>{selected?selected.label:placeholder}</span>
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--ink-faint)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{transform:open?'rotate(180deg)':'none',transition:'transform .15s'}}>
          <path d="m6 9 6 6 6-6"/>
        </svg>
      </div>
      {open && (
        <div className="card" style={{position:'absolute',top:'calc(100% + 4px)',left:0,right:0,zIndex:100,maxHeight:280,overflow:'auto',padding:'6px 0'}}>
          {searchable && (
            <div style={{padding:'6px 12px 8px',borderBottom:'1px solid var(--line)',position:'sticky',top:0,background:'var(--surface)'}}>
              <input
                ref={inputRef}
                className="input mono"
                placeholder="Search…"
                value={query}
                onChange={e=>setQuery(e.target.value)}
                style={{fontSize:12,padding:'6px 10px'}}
                onClick={e=>e.stopPropagation()}
              />
            </div>
          )}
          {filtered.map(opt => (
            <div
              key={opt.value}
              onClick={()=>{ onChange(opt.value); setOpen(false); }}
              style={{
                padding:'8px 12px',fontSize:13,cursor:'pointer',
                color: opt.value===value?'var(--accent-2)':'var(--ink)',
                background: opt.value===value?'rgba(99,102,241,.12)':'transparent',
              }}
              onMouseEnter={e=>{ if(opt.value!==value) e.currentTarget.style.background='rgba(255,255,255,.04)'; }}
              onMouseLeave={e=>{ if(opt.value!==value) e.currentTarget.style.background='transparent'; }}
            >
              {opt.label}
            </div>
          ))}
          {filtered.length===0 && <div style={{padding:'10px 12px',fontSize:12,color:'var(--ink-faint)'}}>No matches</div>}
        </div>
      )}
    </div>
  );
}
window.CustomSelect = CustomSelect;

/* ── Multi-diagram selector with search ── */
function MultiDiagramSelector({ diagramOptions, selected, onChange }) {
  const [query, setQuery] = React.useState('');
  const filtered = query.trim()
    ? diagramOptions.filter(d => d.toLowerCase().includes(query.toLowerCase()))
    : diagramOptions;
  const allFilteredSelected = filtered.length > 0 && filtered.every(d => selected.includes(d));

  return (
    <div className="field" style={{gridColumn:'1/-1'}}>
      <label>Select target diagrams</label>
      <div style={{border:'1px solid var(--line-2)',borderRadius:8,overflow:'hidden'}}>
        {/* Search bar */}
        <div style={{padding:10,borderBottom:'1px solid var(--line-2)',display:'flex',gap:8,alignItems:'center'}}>
          <input
            className="input mono"
            placeholder="Search diagrams…"
            value={query}
            onChange={e=>setQuery(e.target.value)}
            style={{flex:1,fontSize:12,padding:'6px 10px'}}
          />
          {filtered.length > 0 && (
            <button
              className="btn btn-ghost btn-sm"
              onClick={()=>{
                if (allFilteredSelected) {
                  onChange(selected.filter(s => !filtered.includes(s)));
                } else {
                  onChange([...new Set([...selected, ...filtered])]);
                }
              }}
            >
              {allFilteredSelected ? 'Deselect all' : 'Select all'}
            </button>
          )}
        </div>
        {/* Checkbox grid */}
        <div style={{display:'flex',flexWrap:'wrap',gap:8,maxHeight:180,overflow:'auto',padding:10}}>
          {filtered.map(d=> (
            <label key={d} style={{display:'flex',alignItems:'center',gap:6,cursor:'pointer',fontSize:13,padding:'4px 8px',borderRadius:6,background:selected.includes(d)?'rgba(99,102,241,.12)':'rgba(255,255,255,.03)',border:'1px solid '+(selected.includes(d)?'var(--primary)':'var(--line)')}}>
              <input type="checkbox" checked={selected.includes(d)} onChange={e=> {
                const checked = e.target.checked;
                onChange(checked ? [...selected,d] : selected.filter(x=>x!==d));
              }}/>
              <span className="mono">{d}</span>
            </label>
          ))}
          {filtered.length===0 && <span style={{color:'var(--ink-faint)',fontSize:12}}>No diagrams match “{query}”</span>}
        </div>
        {/* Selected count */}
        {selected.length > 0 && (
          <div style={{padding:'6px 10px',borderTop:'1px solid var(--line-2)',fontSize:11,color:'var(--ink-faint)',fontFamily:'Geist Mono,monospace'}}>
            {selected.length} selected
          </div>
        )}
      </div>
    </div>
  );
}

function Spark({ points, color='#22D3EE', w=84, h=28 }) {
  const max = Math.max(...points), min = Math.min(...points);
  const d = points.map((v,i)=>`${i===0?'M':'L'} ${((i/(points.length-1))*w).toFixed(1)} ${(h-((v-min)/(max-min||1))*h).toFixed(1)}`).join(' ');
  return (
    <svg width={w} height={h} className="kpi-spark">
      <path d={d} stroke={color} strokeWidth="1.4" fill="none" strokeLinejoin="round" strokeLinecap="round"/>
      <path d={`${d} L ${w} ${h} L 0 ${h} Z`} fill={color} opacity=".10"/>
    </svg>
  );
}

function Loader() {
  return <div style={{padding:40,textAlign:'center',color:'var(--ink-faint)',fontFamily:'Geist Mono,monospace',fontSize:13}}>Loading…</div>;
}

function fmtTime(seconds) {
  if (!seconds) return '—';
  const s = Math.round(seconds);
  if (s < 60) return s + 's';
  if (s < 3600) return Math.floor(s/60) + 'm ' + (s%60) + 's';
  return Math.floor(s/3600) + 'h ' + Math.floor((s%3600)/60) + 'm';
}

function fmtDate(ts) {
  if (!ts) return '—';
  try { return new Date(ts).toLocaleString(); } catch(e) { return ts; }
}

/* ══════════════════════════════════════════════════════════════
   DASHBOARD
   ══════════════════════════════════════════════════════════ */
function DashboardPage({ onView }) {
  const auth = useAuth();
  const [instances, setInstances] = React.useState([]);
  const [diagrams,  setDiagrams]  = React.useState([]);
  const [loading,   setLoading]   = React.useState(true);

  React.useEffect(() => {
    if (!auth.tenantId) return;
    Promise.all([
      apiFetch('/instances').then(r=>r?.json()).catch(()=>[]),
      apiFetch('/api/v1/tenants/'+auth.tenantId+'/diagrams').then(r=>r?.json()).catch(()=>[]),
    ]).then(([insts, diags]) => {
      setInstances(Array.isArray(insts) ? insts : []);
      setDiagrams(Array.isArray(diags) ? diags : []);
      setLoading(false);
    });
  }, [auth.tenantId]);

  const running   = instances.filter(i=>i.status==='running').length;
  const waiting   = instances.filter(i=>i.status==='waiting').length;
  const active    = running + waiting;
  const recent    = instances.slice(0, 5);

  if (loading) return <Loader/>;

  return (
    <div className="fade-up">
      <PageHead
        title="Dashboard"
        sub={'Overview of your workflow runtime · ' + (auth.tenantId||'').slice(0,8)}
        actions={<>
          <button className="btn btn-ghost btn-sm" onClick={()=>window.location.reload()}><I name="refresh" size={13}/>Reload</button>
        </>}
      />

      {/* Status row */}
      <div style={{display:'grid',gridTemplateColumns:'1.4fr 1fr 1fr',gap:14,marginBottom:14}}>
        <div className="card card-pad" style={{display:'flex',alignItems:'center',gap:18}}>
          <div style={{width:44,height:44,borderRadius:10,background:'rgba(52,211,153,.10)',border:'1px solid rgba(52,211,153,.25)',display:'flex',alignItems:'center',justifyContent:'center',color:'var(--success)'}}>
            <I name="check" size={20}/>
          </div>
          <div style={{flex:1}}>
            <div style={{fontSize:11,fontFamily:'Geist Mono,monospace',letterSpacing:'.08em',textTransform:'uppercase',color:'var(--ink-faint)'}}>System health</div>
            <div style={{fontSize:17,fontWeight:600,marginTop:2,display:'flex',alignItems:'center',gap:8}}>All systems operational <Badge kind="healthy">healthy</Badge></div>
            <div style={{color:'var(--ink-faint)',fontSize:12,fontFamily:'Geist Mono,monospace',marginTop:4}}>backend · logic-engine</div>
          </div>
        </div>
        <div className="card card-pad">
          <div style={{display:'flex',alignItems:'center',gap:10}}><I name="diagram" size={16}/><span style={{fontSize:11,fontFamily:'Geist Mono,monospace',letterSpacing:'.08em',textTransform:'uppercase',color:'var(--ink-faint)'}}>Diagrams</span></div>
          <div style={{fontSize:30,fontWeight:600,letterSpacing:'-.025em',margin:'6px 0 8px'}}>{diagrams.length} <span style={{fontSize:13,color:'var(--ink-faint)',fontFamily:'Geist Mono,monospace'}}>total</span></div>
        </div>
        <div className="card card-pad">
          <div style={{display:'flex',alignItems:'center',gap:10}}><I name="play" size={16}/><span style={{fontSize:11,fontFamily:'Geist Mono,monospace',letterSpacing:'.08em',textTransform:'uppercase',color:'var(--ink-faint)'}}>Instances</span></div>
          <div style={{fontSize:30,fontWeight:600,letterSpacing:'-.025em',margin:'6px 0 8px'}}>{active} <span style={{fontSize:13,color:'var(--accent)',fontFamily:'Geist Mono,monospace'}}>active</span></div>
        </div>
      </div>

      {/* KPI */}
      <div style={{margin:'22px 0 12px',display:'flex',alignItems:'baseline',justifyContent:'space-between'}}>
        <div>
          <h2 style={{margin:0,fontSize:15,fontWeight:600,letterSpacing:'-.01em'}}>Live instances</h2>
          <p style={{margin:'2px 0 0',color:'var(--ink-faint)',fontSize:12,fontFamily:'Geist Mono,monospace'}}>running + waiting</p>
        </div>
      </div>
      <div className="kpi-grid">
        {[
          { lbl:'Running',   val:running,  color:'#22D3EE', spark:[1,2,2,3,2,4,3,running||0,running||0,running||0] },
          { lbl:'Waiting',   val:waiting,  color:'#FBBF24', spark:[2,3,2,4,3,2,3,waiting||0,waiting||0,waiting||0] },
          { lbl:'Total active', val:active, color:'#818CF8',spark:[3,5,4,6,5,6,active||0,active||0,active||0,active||0] },
          { lbl:'Diagrams',  val:diagrams.length, color:'#34D399', spark:[4,5,6,7,diagrams.length||0,diagrams.length||0,diagrams.length||0,diagrams.length||0,diagrams.length||0,diagrams.length||0] },
        ].map(k => (
          <div key={k.lbl} className="kpi">
            <div className="lbl"><I name="bolt" size={11}/>{k.lbl}</div>
            <div className="val">{k.val}</div>
            <Spark points={k.spark} color={k.color}/>
          </div>
        ))}
      </div>

      {/* Recent instances table */}
      <div className="card" style={{marginTop:22}}>
        <div className="card-head">
          <h3><I name="play" size={14}/>Active instances</h3>
          <div className="sub">{instances.length} total</div>
        </div>
        {instances.length === 0
          ? <div className="empty" style={{margin:14}}>No active instances</div>
          : (
            <table className="tbl">
              <thead><tr><th>Instance ID</th><th>Diagram</th><th>Current node</th><th>Status</th><th>Time remaining</th><th className="right">Action</th></tr></thead>
              <tbody>
                {recent.map(r => (
                  <tr key={r.instance_id||r.id}>
                    <td className="mono" style={{fontSize:12.5}}>{r.instance_id||r.id}</td>
                    <td className="mono" style={{fontSize:12.5,color:'var(--accent-2)'}}>{r.diagram_id||r.diagram_name||'—'}</td>
                    <td className="mono" style={{fontSize:12,color:'var(--ink-dim)'}}>{r.current_node_id||'—'}</td>
                    <td><Badge kind={r.status}>{r.status}</Badge></td>
                    <td className="mono" style={{fontSize:12.5,color:'var(--ink-dim)'}}>{fmtTime(r.time_remaining||r.ttl_remaining)}</td>
                    <td className="right"><button className="btn btn-ghost btn-sm" onClick={()=>onView&&onView(r.instance_id||r.id)}>View</button></td>
                  </tr>
                ))}
              </tbody>
            </table>
          )
        }
      </div>
    </div>
  );
}
window.DashboardPage = DashboardPage;

/* ══════════════════════════════════════════════════════════════
   DIAGRAMS
   ══════════════════════════════════════════════════════════ */
function MiniDiagramThumb({ seed=0 }) {
  return (
    <svg viewBox="0 0 240 88" width="100%" height="100%" preserveAspectRatio="xMidYMid slice">
      <defs><pattern id={`mdt-${seed}`} width="14" height="14" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r=".6" fill="rgba(255,255,255,0.06)"/></pattern></defs>
      <rect width="240" height="88" fill={`url(#mdt-${seed})`}/>
      <g transform={`translate(${10+(seed%3)*4} 22)`}>
        <rect width="46" height="36" rx="6" fill="#0F1322" stroke="#22D3EE" strokeOpacity=".5"/>
        <circle cx="10" cy="18" r="3" fill="#22D3EE"/>
        <rect x="18" y="13" width="22" height="3" rx="1.5" fill="#EDEFF7" opacity=".7"/>
        <rect x="18" y="20" width="14" height="2" rx="1" fill="#5B6378"/>
      </g>
      <path d="M 64 40 L 92 40" stroke="#6366F1" strokeWidth="1.3" strokeDasharray="3 3"/>
      <g transform={`translate(${94+(seed%2)*6} 22)`}>
        <rect width="50" height="36" rx="6" fill="#13162A" stroke="#818CF8" strokeOpacity=".5"/>
        <circle cx="10" cy="18" r="3" fill="#818CF8"/>
        <rect x="18" y="13" width="22" height="3" rx="1.5" fill="#EDEFF7" opacity=".7"/>
        <rect x="18" y="20" width="18" height="2" rx="1" fill="#5B6378"/>
      </g>
      <path d="M 152 40 L 180 40" stroke="#34D399" strokeWidth="1.3" strokeDasharray="3 3"/>
      <g transform={`translate(${182+(seed%2)*4} 22)`}>
        <rect width="46" height="36" rx="6" fill="#0F1F1C" stroke="#34D399" strokeOpacity=".55"/>
        <circle cx="10" cy="18" r="3" fill="#34D399"/>
        <rect x="18" y="13" width="22" height="3" rx="1.5" fill="#EDEFF7" opacity=".7"/>
        <rect x="18" y="20" width="14" height="2" rx="1" fill="#5B6378"/>
      </g>
    </svg>
  );
}

function DiagramsPage() {
  const auth = useAuth();
  const [diagrams, setDiagrams] = React.useState([]);
  const [loading,  setLoading]  = React.useState(true);
  const [q, setQ]               = React.useState('');
  const [busy, setBusy]         = React.useState(null);

  const load = () => {
    if (!auth.tenantId) return;
    setLoading(true);
    apiFetch('/api/v1/tenants/'+auth.tenantId+'/diagrams')
      .then(r=>r?.json()).then(d=>{ setDiagrams(Array.isArray(d)?d:[]); setLoading(false); })
      .catch(()=>setLoading(false));
  };
  React.useEffect(load, [auth.tenantId]);

  const deleteDiagram = async (name) => {
    const ok = window.modalConfirm ? await window.modalConfirm('Delete diagram "'+name+'"? This cannot be undone.') : confirm('Delete diagram "'+name+'"? This cannot be undone.');
    if (!ok) return;
    setBusy(name);
    await apiFetch('/api/v1/tenants/'+auth.tenantId+'/diagrams/'+encodeURIComponent(name), { method:'DELETE' });
    setBusy(null); load();
  };

  const runDiagram = async (name) => {
    const key = window.modalPrompt ? await window.modalPrompt('Instantiation key for "'+name+'" (e.g. order-123, truck-01):\nLeave blank to auto-generate.', '') : prompt('Instantiation key for "'+name+'" (e.g. order-123, truck-01):\nLeave blank to auto-generate.');
    if (key === null) return;
    setBusy(name);
    const payload = (key||'').trim() ? { key: key.trim() } : {};
    const res = await apiFetch('/api/v1/tenants/'+auth.tenantId+'/diagrams/'+encodeURIComponent(name)+'/instantiate', {
      method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(payload),
    });
    setBusy(null);
    if (res?.ok || res?.status === 201) {
      const data = await res.json().catch(()=>({}));
      const msg = 'Instance created: ' + (data.instance_id||'OK');
      if (window.modalAlert) window.modalAlert(msg); else alert(msg);
    } else {
      const err = await res?.json().catch(()=>({}));
      const msg = 'Error: ' + (err.detail||'Failed to instantiate');
      if (window.modalAlert) window.modalAlert(msg); else alert(msg);
    }
  };

  const editDiagram = (name) => {
    window.__EDITOR_LOAD_NAME__ = name;
    if (window.setAppPage) window.setAppPage('editor');
  };

  const uploadJSON = () => {
    const input = document.createElement('input');
    input.type = 'file'; input.accept = '.json,application/json';
    input.onchange = async (e) => {
      const file = e.target.files[0];
      if (!file) return;
      const text = await file.text();
      let parsed;
      try { parsed = JSON.parse(text); } catch(e) { if(window.modalAlert)window.modalAlert('Invalid JSON'); else alert('Invalid JSON'); return; }
      const body = Array.isArray(parsed) ? parsed : [parsed];
      const res = await apiFetch('/api/v1/tenants/'+auth.tenantId+'/diagrams', {
        method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body),
      });
      if (res?.ok || res?.status === 207) { const msg='Diagram(s) uploaded!'; if(window.modalAlert)window.modalAlert(msg); else alert(msg); load(); }
      else { const err = await res?.json().catch(()=>({})); const msg='Upload failed: '+(err.detail||res?.status); if(window.modalAlert)window.modalAlert(msg); else alert(msg); }
    };
    input.click();
  };

  const filtered = diagrams.filter(d=>(d.diagram_name||'').toLowerCase().includes(q.toLowerCase()));

  return (
    <div className="fade-up">
      <PageHead
        title="Diagrams"
        sub={diagrams.length+' workflow definitions'}
        actions={<>
          <div style={{display:'flex',alignItems:'center',gap:8,padding:'0 10px',height:34,border:'1px solid var(--line-2)',borderRadius:8,background:'rgba(255,255,255,.02)'}}>
            <I name="search" size={14}/>
            <input value={q} onChange={e=>setQ(e.target.value)} placeholder="Search diagrams…" style={{background:'transparent',border:0,outline:'none',color:'var(--ink)',font:'inherit',fontSize:13,width:180}}/>
          </div>
          <button className="btn btn-ghost" onClick={uploadJSON}><I name="upload" size={14}/>Upload JSON</button>
          <button className="btn btn-ghost" onClick={load}><I name="refresh" size={14}/>Refresh</button>
        </>}
      />

      {loading ? <Loader/> : filtered.length === 0
        ? <div className="empty">No diagrams found{q&&' matching "'+q+'"'}</div>
        : (
          <div className="dgrid">
            {filtered.map((d,i) => (
              <div key={d.diagram_name||i} className="d-card">
                <div className="head">
                  <div>
                    <div className="name">{d.diagram_name}</div>
                    <div className="meta">v{d.version||'?'} · <Badge kind="active">active</Badge></div>
                  </div>
                </div>
                <div className="d-thumb"><MiniDiagramThumb seed={i}/></div>
                <div className="id">{d.diagram_id||d.tenant_id||''}</div>
                <div className="row-actions">
                  <Badge kind="neutral" dot={false}>tenant diagram</Badge>
                  <div style={{display:'flex',gap:8}}>
                    <button className="icon-btn" title="Run" aria-label="Run" style={{color:'var(--success)',padding:6}} onClick={()=>runDiagram(d.diagram_name)} disabled={busy===d.diagram_name}><I name="play" size={16}/></button>
                    <button className="icon-btn" title="Edit" aria-label="Edit" style={{padding:6}} onClick={()=>editDiagram(d.diagram_name)} disabled={busy===d.diagram_name}><I name="pencil" size={16}/></button>
                    <button className="icon-btn" title="Delete" aria-label="Delete" style={{color:'var(--danger)',padding:6}} onClick={()=>deleteDiagram(d.diagram_name)} disabled={busy===d.diagram_name}><I name="trash" size={16}/></button>
                  </div>
                </div>
              </div>
            ))}
          </div>
        )
      }
    </div>
  );
}
window.DiagramsPage = DiagramsPage;

/* ══════════════════════════════════════════════════════════════
   INSTANCES
   ══════════════════════════════════════════════════════════ */
function InstancesPage({ onView }) {
  const auth = useAuth();
  const [instances, setInstances] = React.useState([]);
  const [loading,   setLoading]   = React.useState(true);
  const [tab,       setTab]       = React.useState('all');
  const [busy,      setBusy]      = React.useState(null);

  const load = React.useCallback(() => {
    apiFetch('/instances').then(r=>r?.json())
      .then(d=>{ setInstances(Array.isArray(d)?d:[]); setLoading(false); })
      .catch(()=>setLoading(false));
  }, []);
  React.useEffect(() => {
    setLoading(true); load();
    // Auto-refresh every 15s so expired/completed instances disappear
    const t = setInterval(load, 15000);
    return () => clearInterval(t);
  }, [load]);

  // Normalize status: backend now returns status, but fallback to is_waiting/is_paused
  const withStatus = instances.map(i => ({
    ...i,
    status: i.status || (i.is_paused ? 'paused' : i.is_waiting ? 'waiting' : 'running'),
    diagram_name: i.diagram_name || i.diagram_id,
    instantiation_key: i.instantiation_key || i.instantiation_key_value,
  }));

  const filtered = tab === 'all' ? withStatus : withStatus.filter(i=>i.status===tab);

  const tabCounts = {
    all: withStatus.length,
    running: withStatus.filter(i=>i.status==='running').length,
    waiting: withStatus.filter(i=>i.status==='waiting').length,
    paused:  withStatus.filter(i=>i.status==='paused').length,
    completed: withStatus.filter(i=>i.status==='completed').length,
    failed: withStatus.filter(i=>i.status==='failed').length,
  };

  const terminate = async (id) => {
    const ok = window.modalConfirm ? await window.modalConfirm('Terminate instance "'+id+'"?') : confirm('Terminate instance "'+id+'"?');
    if (!ok) return;
    setBusy(id);
    await apiFetch('/instances/'+encodeURIComponent(id), { method:'DELETE' });
    setBusy(null); load();
  };

  const pause = async (id) => {
    setBusy(id);
    await apiFetch('/instances/'+encodeURIComponent(id)+'/pause', { method:'POST' });
    setBusy(null); load();
  };

  const resume = async (id) => {
    setBusy(id);
    await apiFetch('/instances/'+encodeURIComponent(id)+'/resume', { method:'POST' });
    setBusy(null); load();
  };

  return (
    <div className="fade-up">
      <PageHead
        title="Instances"
        sub="Live workflow instances"
        actions={<>
          <button className="btn btn-ghost btn-sm" onClick={load}><I name="refresh" size={13}/>Refresh</button>
        </>}
      />

      <div style={{display:'flex',gap:8,marginBottom:14,flexWrap:'wrap'}}>
        {['all','running','waiting','paused','completed','failed'].map(t => (
          <button key={t} className="btn btn-sm" onClick={()=>setTab(t)}
            style={{background:tab===t?'rgba(255,255,255,.06)':'transparent',border:`1px solid ${tab===t?'var(--line-2)':'var(--line)'}`,color:tab===t?'var(--ink)':'var(--ink-dim)',textTransform:'capitalize'}}>
            {t} <span className="mono" style={{fontSize:10.5,color:'var(--ink-faint)',marginLeft:4}}>{tabCounts[t]||0}</span>
          </button>
        ))}
      </div>

      {loading ? <Loader/> : (
        <div className="card">
          {filtered.length === 0
            ? <div className="empty" style={{margin:14}}>No instances{tab!=='all'?' with status "'+tab+'"':''}</div>
            : (
              <table className="tbl">
                <thead><tr><th>Instance ID</th><th>Diagram</th><th>Current node</th><th>Status</th><th>Time remaining</th><th className="right">Actions</th></tr></thead>
                <tbody>
                  {filtered.map(r => {
                    const id = r.instance_id||r.id;
                    return (
                      <tr key={id}>
                        <td className="mono" style={{fontSize:12.5}}>{id}</td>
                        <td><span className="mono" style={{fontSize:12.5,color:'var(--accent-2)'}}>{r.diagram_name||r.diagram_id||'—'}</span></td>
                        <td className="mono" style={{fontSize:12,color:'var(--ink-dim)'}}>{r.current_node_id||'—'}</td>
                        <td><Badge kind={r.status}>{r.status}</Badge></td>
                        <td className="mono" style={{fontSize:12.5,color:'var(--ink-dim)'}}>{fmtTime(r.time_remaining||r.ttl_remaining)}</td>
                        <td>
                          <div className="actions">
                            <button className="icon-btn" title="View" aria-label="View" onClick={()=>onView&&onView(id)} disabled={busy===id}><I name="eye" size={14}/></button>
                            {r.status==='running'
                              ? <button className="icon-btn" title="Pause" aria-label="Pause" style={{color:'var(--warning)'}} onClick={()=>pause(id)} disabled={busy===id}><I name="pause" size={14}/></button>
                              : (r.status==='waiting'||r.status==='paused')
                              ? <button className="icon-btn" title="Resume" aria-label="Resume" style={{color:'var(--success)'}} onClick={()=>resume(id)} disabled={busy===id}><I name="play" size={14}/></button>
                              : null
                            }
                            <button className="icon-btn" title="Terminate" aria-label="Terminate" style={{color:'var(--danger)'}} onClick={()=>terminate(id)} disabled={busy===id}><I name="stop" size={14}/></button>
                          </div>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            )
          }
        </div>
      )}
    </div>
  );
}
window.InstancesPage = InstancesPage;

/* ══════════════════════════════════════════════════════════════
   INSTANCE HISTORY
   ══════════════════════════════════════════════════════════ */
function HistoryPage() {
  const [rows,      setRows]      = React.useState([]);
  const [loading,   setLoading]   = React.useState(true);  // start loading immediately
  const [diagramId, setDiagramId] = React.useState('');
  const [status,    setStatus]    = React.useState('');
  const [search,    setSearch]    = React.useState('');
  const [limit,     setLimit]     = React.useState('100');
  const [fetched,   setFetched]   = React.useState(false);
  const [err,       setErr]       = React.useState('');

  const fetch_ = React.useCallback((opts) => {
    const dId = (opts && opts.diagramId !== undefined) ? opts.diagramId : diagramId;
    const st  = (opts && opts.status    !== undefined) ? opts.status    : status;
    const q   = (opts && opts.search    !== undefined) ? opts.search    : search;
    const lim = (opts && opts.limit     !== undefined) ? opts.limit     : limit;
    setLoading(true); setErr('');
    const p = new URLSearchParams();
    if (dId.trim()) p.set('diagram_id', dId.trim());
    if (st.trim())  p.set('status', st.trim());
    if (q.trim())   p.set('search', q.trim());
    if (lim)        p.set('limit', lim);
    apiFetch('/history/instances?' + p.toString())
      .then(r => {
        if (!r) { setErr('No response from server'); setLoading(false); setFetched(true); return; }
        if (!r.ok) { r.json().then(e=>setErr(e.detail||'Error loading history')).catch(()=>setErr('Error '+r.status)); setLoading(false); setFetched(true); return; }
        return r.json();
      })
      .then(d => { if(d!==undefined){ setRows(Array.isArray(d)?d:[]); setLoading(false); setFetched(true); } })
      .catch(e => { setErr('Failed to load history'); setLoading(false); setFetched(true); });
  }, [diagramId, status, search, limit]);

  // Auto-load on mount
  React.useEffect(() => { fetch_({diagramId:'',status:'',search:'',limit:'100'}); }, []);

  return (
    <div className="fade-up">
      <PageHead title="Instance execution history" sub="Replay, inspect, and audit every workflow run"/>
      <div className="filters">
        <div className="field">
          <label>Diagram ID</label>
          <input className="input" placeholder="speeding" value={diagramId} onChange={e=>setDiagramId(e.target.value)} onKeyDown={e=>e.key==='Enter'&&fetch_()}/>
        </div>
        <div className="field">
          <label>Status</label>
          <CustomSelect
            value={status}
            onChange={v=>setStatus(v)}
            options={[
              {value:'',label:'All'},
              {value:'completed',label:'completed'},
              {value:'error',label:'error'},
              {value:'ttl_expired',label:'ttl_expired'},
              {value:'failed',label:'failed'},
            ]}
          />
        </div>
        <div className="field">
          <label>Search</label>
          <input className="input" placeholder="Instance ID · key" value={search} onChange={e=>setSearch(e.target.value)} onKeyDown={e=>e.key==='Enter'&&fetch_()}/>
        </div>
        <div className="field">
          <label>Limit</label>
          <input className="input" value={limit} onChange={e=>setLimit(e.target.value)} style={{width:80}}/>
        </div>
        <button className="btn btn-accent" onClick={()=>fetch_()} disabled={loading}>
          {loading ? '…' : 'Fetch'}
        </button>
      </div>

      {err && <div style={{padding:'12px 16px',marginBottom:14,background:'rgba(248,113,113,.08)',border:'1px solid rgba(248,113,113,.3)',borderRadius:10,color:'var(--danger)',fontSize:13}}>{err}</div>}
      <div className="card">
        {loading ? <Loader/>
          : rows.length === 0
          ? <div className="empty" style={{margin:14}}>{fetched ? 'No records found — try different filters or run a diagram first' : 'Loading…'}</div>
          : (
            <table className="tbl">
              <thead><tr><th>Instance ID</th><th>Diagram</th><th>Key</th><th>Status</th><th>Created</th><th>Terminated</th></tr></thead>
              <tbody>
                {rows.map((r,i) => (
                  <tr key={i}>
                    <td className="mono" style={{fontSize:12.5}}>{r.instance_id||r.id||'—'}</td>
                    <td className="mono" style={{fontSize:12.5,color:'var(--accent-2)'}}>{r.diagram_id||r.diagram_name||'—'}</td>
                    <td className="mono" style={{fontSize:12.5,color:'var(--ink-dim)'}}>{r.instantiation_key_value||r.instantiation_key||'—'}</td>
                    <td><Badge kind={r.status==='ttl_expired'?'ttl':r.status}>{r.status||'—'}</Badge></td>
                    <td className="mono" style={{fontSize:12,color:'var(--ink-dim)'}}>{fmtDate(r.creation_time||r.created_at)}</td>
                    <td className="mono" style={{fontSize:12,color:'var(--ink-dim)'}}>{fmtDate(r.termination_time||r.terminated_at||r.updated_at)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )
        }
      </div>
    </div>
  );
}
window.HistoryPage = HistoryPage;

/* ══════════════════════════════════════════════════════════════
   DIAGRAM EDITOR (static stub — overridden by app-flow.jsx)
   ══════════════════════════════════════════════════════════ */
function EditorPage() {
  return <div style={{padding:40,textAlign:'center',color:'var(--ink-faint)'}}>Loading editor…</div>;
}
window.EditorPage = EditorPage;

/* ══════════════════════════════════════════════════════════════
   TENANT PAGE — wrapper
   ══════════════════════════════════════════════════════════ */
function TenantPage({ tab, setTab }) {
  const auth = useAuth();
  return (
    <div className="fade-up">
      <PageHead title="Tenant administration" sub={<span>Managing tenant <span className="mono" style={{color:'var(--accent-2)'}}>{auth.tenantId}</span></span>}/>
      <div className="tabs">
        {[
          { id:'details',  label:'Tenant details' },
          { id:'users',    label:'User management' },
          { id:'webhooks', label:'Webhooks' },
          { id:'billing',  label:'Billing' },
        ].map(t => (
          <div key={t.id} className={'tab'+(tab===t.id?' active':'')} onClick={()=>setTab(t.id)}>{t.label}</div>
        ))}
      </div>
      {tab==='details'  && <TenantDetails/>}
      {tab==='users'    && <TenantUsers/>}
      {tab==='webhooks' && <TenantWebhooks/>}
      {tab==='billing'  && <TenantBilling/>}
    </div>
  );
}
window.TenantPage = TenantPage;

/* ── Tenant Details ──────────────────────────────────────────── */
function TenantDetails() {
  const auth = useAuth();
  const [tenant,  setTenant]  = React.useState(null);
  const [name,    setName]    = React.useState('');
  const [saving,  setSaving]  = React.useState(false);
  const [msg,     setMsg]     = React.useState('');

  React.useEffect(() => {
    apiFetch('/api/v1/tenants/'+auth.tenantId)
      .then(r=>r?.json()).then(d=>{ if(d) { setTenant(d); setName(d.tenant_name||''); } })
      .catch(()=>{});
  }, [auth.tenantId]);

  const save = async () => {
    setSaving(true); setMsg('');
    // Platform-level update; regular users may get 403
    const res = await apiFetch('/api/v1/tenants/'+auth.tenantId, {
      method:'PUT', headers:{'Content-Type':'application/json'}, body:JSON.stringify({ tenant_name:name }),
    });
    setSaving(false);
    setMsg(res?.ok ? 'Saved!' : 'Not allowed (requires platform admin)');
    setTimeout(()=>setMsg(''),3000);
  };

  const copy = (v) => { navigator.clipboard?.writeText(v).catch(()=>{}); };

  return (
    <div className="card card-pad" style={{maxWidth:920}}>
      {!tenant ? <Loader/> : <>
        <h3 style={{margin:'0 0 4px',fontSize:15,fontWeight:600,letterSpacing:'-.01em'}}>Tenant information</h3>
        <p style={{margin:'0 0 22px',color:'var(--ink-faint)',fontSize:12.5,fontFamily:'Geist Mono,monospace'}}>metadata · synced from backend</p>
        <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:24,marginBottom:24}}>
          {[
            ['Tenant ID',    tenant.tenant_id],
            ['Created at',   fmtDate(tenant.created_at)],
            ['Last updated', fmtDate(tenant.updated_at)],
            ['Status',       tenant.status||'active'],
          ].map(([k,v])=>(
            <div key={k} className="field">
              <label>{k}</label>
              <div style={{display:'flex',alignItems:'center',gap:8,fontSize:12.5,color:'var(--ink-dim)',background:'rgba(255,255,255,.02)',border:'1px solid var(--line)',borderRadius:8,padding:'9px 12px',fontFamily:'Geist Mono,monospace'}}>
                <span style={{flex:1,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{v}</span>
                <button className="icon-btn" style={{width:22,height:22,border:0,flexShrink:0}} onClick={()=>copy(v)} title="Copy" aria-label="Copy"><I name="copy" size={12}/></button>
              </div>
            </div>
          ))}
        </div>
        <div style={{height:1,background:'var(--line)',margin:'4px 0 22px'}}/>
        <h3 style={{margin:'0 0 14px',fontSize:14,fontWeight:600}}>Edit tenant name</h3>
        <div style={{display:'grid',gridTemplateColumns:'1fr auto',gap:10,alignItems:'flex-end'}}>
          <div className="field">
            <label>Tenant name</label>
            <input className="input" value={name} onChange={e=>setName(e.target.value)} onKeyDown={e=>e.key==='Enter'&&save()}/>
          </div>
          <button className="btn btn-ghost" onClick={save} disabled={saving||name===tenant.tenant_name}>
            {saving ? '…' : msg || 'Save changes'}
          </button>
        </div>
      </>}
    </div>
  );
}

/* ── Tenant Users ────────────────────────────────────────────── */
function TenantUsers() {
  const auth = useAuth();
  const [users,   setUsers]   = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [email,   setEmail]   = React.useState('');
  const [role,    setRole]    = React.useState('member');
  const [adding,  setAdding]  = React.useState(false);

  const load = () => {
    setLoading(true);
    apiFetch('/api/v1/tenants/'+auth.tenantId+'/users')
      .then(r=>r?.json()).then(d=>{ setUsers(Array.isArray(d)?d:[]); setLoading(false); })
      .catch(()=>setLoading(false));
  };
  React.useEffect(load, [auth.tenantId]);

  const addUser = async () => {
    if (!email.trim()) return;
    setAdding(true);
    const res = await apiFetch('/api/v1/tenants/'+auth.tenantId+'/users', {
      method:'POST', headers:{'Content-Type':'application/json'},
      body:JSON.stringify({ email:email.trim(), role }),
    });
    setAdding(false);
    if (res?.ok || res?.status===200 || res?.status===201) { setEmail(''); load(); }
    else { const err = await res?.json().catch(()=>({})); const msg=err.detail||'Failed to add user'; if(window.modalAlert)window.modalAlert(msg); else alert(msg); }
  };

  const removeUser = async (userId) => {
    const ok = window.modalConfirm ? await window.modalConfirm('Remove this user from the tenant?') : confirm('Remove this user from the tenant?');
    if (!ok) return;
    await apiFetch('/api/v1/tenants/'+auth.tenantId+'/users/'+userId, { method:'DELETE' });
    load();
  };

  const leaveTenant = async () => {
    const ok = window.modalConfirm ? await window.modalConfirm('Are you sure you want to leave this tenant?') : confirm('Are you sure you want to leave this tenant?');
    if (!ok) return;
    await apiFetch('/api/v1/tenants/'+auth.tenantId+'/users/'+auth.userId, { method:'DELETE' });
    localStorage.removeItem('fluentra_user');
    window.location.reload();
  };

  return (
    <div style={{display:'flex',flexDirection:'column',gap:14}}>
      {/* Add user form */}
      <div className="card card-pad">
        <h3 style={{margin:'0 0 14px',fontSize:14,fontWeight:600,display:'flex',alignItems:'center',gap:8}}><I name="users" size={14}/>Add user</h3>
        <div style={{display:'grid',gridTemplateColumns:'1fr auto auto',gap:10,alignItems:'flex-end'}}>
          <div className="field">
            <label>Email address</label>
            <input className="input" type="email" placeholder="user@example.com" value={email} onChange={e=>setEmail(e.target.value)}/>
          </div>
          <div className="field">
            <label>Role</label>
            <CustomSelect
              value={role}
              onChange={v=>setRole(v)}
              options={[
                {value:'member',label:'member'},
                {value:'admin',label:'admin'},
              ]}
            />
          </div>
          <button className="btn btn-accent" onClick={addUser} disabled={adding||!email.trim()}>{adding?'…':'Add / Invite'}</button>
        </div>
      </div>

      {/* Leave tenant */}
      {(() => {
        const me = users.find(u => u.user_id === auth.userId);
        if (me && me.role !== 'owner') {
          return (
            <div className="card card-pad">
              <button className="btn" style={{background:'rgba(248,113,113,.10)',color:'var(--danger)',border:'1px solid rgba(248,113,113,.3)',width:'100%'}} onClick={leaveTenant}>
                <I name="logout" size={14}/> Leave Tenant
              </button>
            </div>
          );
        }
        return null;
      })()}

      {/* Users table */}
      <div className="card">
        <div className="card-head"><h3><I name="users" size={14}/>Team members</h3><span className="sub">{users.length} members</span></div>
        {loading ? <Loader/> : users.length === 0
          ? <div className="empty" style={{margin:14}}>No users found</div>
          : (
            <table className="tbl">
              <thead><tr><th>Email</th><th>Role</th><th>Status</th><th className="right">Actions</th></tr></thead>
              <tbody>
                {users.map((u,i) => (
                  <tr key={u.user_id||i}>
                    <td style={{display:'flex',alignItems:'center',gap:10}}>
                      <div className="avatar">{(u.email||'?')[0].toUpperCase()}</div>
                      <span>{u.email||u.user_id}</span>
                    </td>
                    <td><Badge kind={u.role==='owner'?'warn':u.role==='admin'?'running':'neutral'} dot={false}>{u.role||'member'}</Badge></td>
                    <td><Badge kind={u.status==='active'?'active':u.status==='pending'?'warn':'neutral'}>{u.status||'active'}</Badge></td>
                    <td>
                      <div className="actions">
                        <button className="icon-btn" style={{color:'var(--danger)'}} onClick={()=>removeUser(u.user_id)} title="Remove" aria-label="Remove"><I name="trash" size={13}/></button>
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )
        }
      </div>
    </div>
  );
}

/* ── Tenant Webhooks ─────────────────────────────────────────── */
function TenantWebhooks() {
  const auth = useAuth();
  const [endpoints, setEndpoints] = React.useState([]);
  const [diagrams,  setDiagrams]  = React.useState([]);
  const [loading,   setLoading]   = React.useState(true);
  const [showForm,  setShowForm]  = React.useState(false);
  const [isEdit,    setIsEdit]    = React.useState(false);
  const [editId,    setEditId]    = React.useState(null);
  const [form, setForm] = React.useState({ path_segment:'', target_diagram_name:'', target_diagram_names:[], target_all_diagrams:false, description:'', webhook_secret:'', target_mode:'single' });
  const [saving, setSaving] = React.useState(false);
  const [toast, setToast] = React.useState(null);

  const load = () => {
    setLoading(true);
    apiFetch('/api/v1/tenants/'+auth.tenantId+'/event_endpoints')
      .then(r=>r?.json()).then(d=>{ setEndpoints(Array.isArray(d)?d:[]); setLoading(false); })
      .catch(()=>setLoading(false));
  };
  const loadDiagrams = () => {
    apiFetch('/api/v1/tenants/'+auth.tenantId+'/diagrams')
      .then(r=>r?.json()).then(d=>{ setDiagrams(Array.isArray(d)?d:[]); })
      .catch(()=>setDiagrams([]));
  };
  React.useEffect(() => { load(); loadDiagrams(); }, [auth.tenantId]);

  const webhookUrl = (seg) => window.location.origin + '/t/' + auth.tenantId + '/' + seg;
  const copy = (v) => {
    navigator.clipboard?.writeText(v).catch(()=>{});
    setToast({msg:'Webhook URL copied', ok:true});
    setTimeout(()=>setToast(null), 2500);
  };

  const resetForm = () => {
    setForm({ path_segment:'', target_diagram_name:'', target_diagram_names:[], target_all_diagrams:false, description:'', webhook_secret:'', target_mode:'single' });
    setIsEdit(false); setEditId(null);
  };

  const openCreate = () => { resetForm(); setShowForm(true); };
  const openEdit = (ep) => {
    let mode = 'single';
    if (ep.target_all_diagrams) mode = 'all';
    else if (ep.target_diagram_names && ep.target_diagram_names.length > 0) mode = 'multiple';
    setForm({
      path_segment: ep.path_segment||'',
      target_diagram_name: ep.target_diagram_name||'',
      target_diagram_names: ep.target_diagram_names||[],
      target_all_diagrams: !!ep.target_all_diagrams,
      description: ep.description||'',
      webhook_secret: '',
      target_mode: mode,
    });
    setIsEdit(true);
    setEditId(ep.endpoint_id||ep.id);
    setShowForm(true);
  };

  const validatePath = (v) => /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(v);

  const buildPayload = () => {
    const payload = { path_segment: form.path_segment.trim(), description: form.description||undefined };
    if (form.target_mode === 'single') {
      payload.target_diagram_name = form.target_diagram_name;
    } else if (form.target_mode === 'multiple') {
      payload.target_diagram_names = form.target_diagram_names;
    } else if (form.target_mode === 'all') {
      payload.target_all_diagrams = true;
    }
    if (form.webhook_secret) payload.webhook_secret = form.webhook_secret;
    return payload;
  };

  const saveEp = async () => {
    if (!form.path_segment.trim()) { setToast({msg:'Path segment is required', ok:false}); return; }
    if (!validatePath(form.path_segment.trim())) { setToast({msg:'Path segment: letters, digits, hyphens, underscores only', ok:false}); return; }
    if (form.target_mode === 'single' && !form.target_diagram_name.trim()) { setToast({msg:'Target diagram name is required', ok:false}); return; }
    if (form.target_mode === 'multiple' && form.target_diagram_names.length === 0) { setToast({msg:'Select at least one target diagram', ok:false}); return; }
    setSaving(true);
    const payload = buildPayload();
    let res;
    if (isEdit && editId) {
      res = await apiFetch('/api/v1/tenants/'+auth.tenantId+'/event_endpoints/'+editId, {
        method:'PUT', headers:{'Content-Type':'application/json'}, body:JSON.stringify(payload),
      });
    } else {
      res = await apiFetch('/api/v1/tenants/'+auth.tenantId+'/event_endpoints', {
        method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(payload),
      });
    }
    setSaving(false);
    if (res?.ok||res?.status===201) {
      setShowForm(false); resetForm(); load();
      setToast({msg: isEdit ? 'Endpoint updated' : 'Endpoint created', ok:true});
      setTimeout(()=>setToast(null), 2500);
    } else {
      const err = await res?.json().catch(()=>({}));
      setToast({msg: err.detail||'Failed to save endpoint', ok:false});
    }
  };

  const deleteEp = async (id) => {
    const ok = window.modalConfirm ? await window.modalConfirm('Delete this endpoint?') : confirm('Delete this endpoint?');
    if (!ok) return;
    const res = await apiFetch('/api/v1/tenants/'+auth.tenantId+'/event_endpoints/'+id, { method:'DELETE' });
    if (res?.ok||res?.status===204) { load(); setToast({msg:'Endpoint deleted', ok:true}); setTimeout(()=>setToast(null), 2500); }
    else { setToast({msg:'Failed to delete endpoint', ok:false}); }
  };

  const sendTestEvent = async (ep) => {
    const url = webhookUrl(ep.path_segment||ep.path||'');
    try {
      const res = await fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ type: 'test', timestamp: new Date().toISOString(), source: 'fluentra-console' }),
      });
      const text = await res.text();
      const msg = `Status: ${res.status}\n\n${text.slice(0, 800)}`;
      if (window.modalAlert) window.modalAlert(msg); else alert(msg);
    } catch(e) {
      const msg = 'Request failed: ' + e.message;
      if (window.modalAlert) window.modalAlert(msg); else alert(msg);
    }
  };

  const targetDisplay = (ep) => {
    if (ep.target_all_diagrams) return <span style={{color:'var(--accent-2)'}}>All diagrams</span>;
    if (ep.target_diagram_names && ep.target_diagram_names.length > 0) return <span title={ep.target_diagram_names.join(', ')}>Multiple ({ep.target_diagram_names.length})</span>;
    if (ep.target_diagram_name) return <span className="mono" style={{color:'var(--accent-2)'}}>{ep.target_diagram_name}</span>;
    return <span style={{color:'var(--ink-faint)'}}>—</span>;
  };

  const diagramOptions = diagrams.map(d => d.diagram_name).filter(Boolean);

  return (
    <div style={{display:'flex',flexDirection:'column',gap:14}}>
      {/* Toast */}
      {toast && (
        <div className="card card-pad" style={{position:'fixed',top:20,right:20,zIndex:9999,background:toast.ok?'rgba(52,211,153,.12)':'rgba(248,113,113,.12)',borderColor:toast.ok?'rgba(52,211,153,.3)':'rgba(248,113,113,.3)',padding:'10px 16px',fontSize:13}}>
          <span style={{color:toast.ok?'var(--success)':'var(--danger)'}}>{toast.msg}</span>
        </div>
      )}

      {/* Create / Edit Form */}
      {showForm && (
        <div className="card card-pad fade-up">
          <h3 style={{margin:'0 0 14px',fontSize:14,fontWeight:600}}>{isEdit?'Edit':'Create'} event endpoint</h3>
          <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:14,marginBottom:14}}>
            <div className="field">
              <label>Path segment</label>
              <input className="input mono" placeholder="my-webhook" value={form.path_segment} onChange={e=>setForm(f=>({...f,path_segment:e.target.value.replace(/[^a-zA-Z0-9_-]/g,'')}))}/>
              <div className="hint">Appears after /t/{auth.tenantId}/</div>
            </div>
            <div className="field">
              <label>Webhook secret (optional)</label>
              <input className="input mono" type="password" placeholder="Leave blank for no secret" value={form.webhook_secret} onChange={e=>setForm(f=>({...f,webhook_secret:e.target.value}))}/>
              <div className="hint">{isEdit?'Leave blank to keep existing secret':'Optional signature verification'}</div>
            </div>

            {/* Target mode selector */}
            <div className="field" style={{gridColumn:'1/-1'}}>
              <label>Target configuration</label>
              <div style={{display:'flex',gap:10,flexWrap:'wrap'}}>
                {['single','multiple','all'].map(m=> (
                  <label key={m} style={{display:'flex',alignItems:'center',gap:6,cursor:'pointer',padding:'6px 10px',borderRadius:8,border:'1px solid '+(form.target_mode===m?'var(--primary)':'var(--line)'),background:form.target_mode===m?'rgba(99,102,241,.08)':'transparent',fontSize:13}}>
                    <input type="radio" name="target_mode" value={m} checked={form.target_mode===m} onChange={e=>setForm(f=>({...f,target_mode:e.target.value}))} style={{accentColor:'var(--primary)'}}/>
                    {m==='single'?'Single diagram':m==='multiple'?'Multiple diagrams':'All diagrams'}
                  </label>
                ))}
              </div>
            </div>

            {form.target_mode==='single' && (
              <div className="field" style={{gridColumn:'1/-1'}}>
                <label>Target diagram name</label>
                <CustomSelect
                  value={form.target_diagram_name}
                  onChange={v=>setForm(f=>({...f,target_diagram_name:v}))}
                  placeholder="Select a diagram…"
                  options={[{value:'',label:'— None —'},...diagramOptions.map(d=>({value:d,label:d}))]}
                  searchable
                />
              </div>
            )}

            {form.target_mode==='multiple' && (
              <MultiDiagramSelector
                diagramOptions={diagramOptions}
                selected={form.target_diagram_names}
                onChange={vals=>setForm(f=>({...f,target_diagram_names:vals}))}
              />
            )}

            {form.target_mode==='all' && (
              <div className="field" style={{gridColumn:'1/-1'}}>
                <div style={{padding:10,border:'1px dashed var(--line-2)',borderRadius:8,fontSize:13,color:'var(--ink-dim)'}}>
                  This endpoint will trigger <b style={{color:'var(--accent-2)'}}>all active diagrams</b> in this tenant.
                </div>
              </div>
            )}

            <div className="field" style={{gridColumn:'1/-1'}}>
              <label>Description</label>
              <input className="input" placeholder="Optional description" value={form.description} onChange={e=>setForm(f=>({...f,description:e.target.value}))}/>
            </div>
          </div>
          <div style={{display:'flex',gap:8}}>
            <button className="btn btn-accent" onClick={saveEp} disabled={saving}>{saving?'…':(isEdit?'Save changes':'Create endpoint')}</button>
            <button className="btn btn-ghost" onClick={()=>{setShowForm(false); resetForm();}}>Cancel</button>
          </div>
        </div>
      )}

      <div className="card">
        <div className="card-head">
          <h3><I name="link" size={14}/>Event endpoints</h3>
          <div style={{display:'flex',gap:8}}>
            <button className="btn btn-ghost btn-sm" onClick={()=>{load(); loadDiagrams();}}><I name="refresh" size={13}/>Refresh</button>
            <button className="btn btn-accent btn-sm" onClick={openCreate}><I name="plus" size={13}/>Create endpoint</button>
          </div>
        </div>
        {loading ? <Loader/> : endpoints.length === 0
          ? <div className="empty" style={{margin:14}}>No event endpoints configured</div>
          : (
            <table className="tbl">
              <thead><tr><th>Webhook URL</th><th>Target</th><th>Description</th><th>Secret</th><th className="right">Actions</th></tr></thead>
              <tbody>
                {endpoints.map((ep,i) => {
                  const url = webhookUrl(ep.path_segment||ep.path||'');
                  return (
                    <tr key={ep.endpoint_id||i}>
                      <td>
                        <div className="url-cell">
                          <span className="url-txt" title={url}>{url}</span>
                          <button className="icon-btn" style={{width:24,height:24}} onClick={()=>copy(url)} title="Copy" aria-label="Copy"><I name="copy" size={12}/></button>
                        </div>
                      </td>
                      <td>{targetDisplay(ep)}</td>
                      <td className="mono" style={{color:'var(--ink-faint)',fontSize:12}}>{ep.description||'—'}</td>
                      <td><Badge kind={ep.webhook_secret?'active':'warn'} dot={false}>{ep.webhook_secret?'Yes':'No'}</Badge></td>
                      <td>
                        <div className="actions">
                          <button className="icon-btn" onClick={()=>sendTestEvent(ep)} title="Send test event" aria-label="Send test event"><I name="bolt" size={13}/></button>
                          <button className="icon-btn" onClick={()=>openEdit(ep)} title="Edit" aria-label="Edit"><I name="pencil" size={13}/></button>
                          <button className="icon-btn" style={{color:'var(--danger)'}} onClick={()=>deleteEp(ep.endpoint_id||ep.id)} title="Delete" aria-label="Delete"><I name="trash" size={13}/></button>
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          )
        }
      </div>
    </div>
  );
}

/* ── Tenant Billing ──────────────────────────────────────────── */
function TenantBilling() {
  const auth = useAuth();
  const [sub,     setSub]     = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [refreshing, setRefreshing] = React.useState(false);

  const load = () => {
    setRefreshing(true);
    apiFetch('/api/v1/tenants/'+auth.tenantId+'/subscription-details')
      .then(r=>r?.json()).then(d=>{ setSub(d); setLoading(false); setRefreshing(false); })
      .catch(()=>{ setLoading(false); setRefreshing(false); });
  };
  React.useEffect(load, [auth.tenantId]);

  const plans = [
    { id:'test',    name:'Test',    price:'$0',   limit:'5 / month',      features:['5 instantiations / month','1 workflow','7-day history','Community support'] },
    { id:'poc',     name:'POC',     price:'$50',  limit:'50 / month',     features:['50 instantiations / month','3 workflows','30-day history','Email support'] },
    { id:'starter', name:'Starter', price:'$500', limit:'10 000 / month', features:['10 000 instantiations / month','Unlimited workflows','90-day history','Slack + email support','Audit logs'], featured:true },
  ];

  const currentPlanId = (sub?.plan_name||'test').toLowerCase();
  const usage   = sub?.current_diagram_instantiation_count ?? 0;
  const limit   = sub?.diagram_instantiation_limit_per_month ?? 5;
  const overage = sub?.effective_overage_allowance ?? 0;
  const totalAllowed = limit + overage;
  const pct     = Math.min(100, Math.round((usage/Math.max(1,totalAllowed))*100));
  const status  = sub?.is_subscription_active ? 'active' : 'inactive';

  return (
    <>
      {loading ? <Loader/> : (
        <>
          <div style={{display:'grid',gridTemplateColumns:'1fr 1fr 1fr',gap:14,marginBottom:24}}>
            <div className="card card-pad">
              <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:8}}><I name="card" size={14}/><span style={{fontSize:11,fontFamily:'Geist Mono,monospace',letterSpacing:'.08em',textTransform:'uppercase',color:'var(--ink-faint)'}}>Current plan</span></div>
              <div style={{display:'flex',alignItems:'center',gap:10}}>
                <div style={{fontSize:22,fontWeight:600,letterSpacing:'-.02em'}}>{sub?.plan_name||'Test plan'}</div>
                <Badge kind={status==='active'?'active':'error'}>{status}</Badge>
              </div>
              <div style={{color:'var(--ink-dim)',fontSize:13,marginTop:4}}>${sub?.price_per_month??0} / month · {limit.toLocaleString()} instantiations / month</div>
            </div>
            <div className="card card-pad">
              <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:8}}><I name="bolt" size={14}/><span style={{fontSize:11,fontFamily:'Geist Mono,monospace',letterSpacing:'.08em',textTransform:'uppercase',color:'var(--ink-faint)'}}>Usage this period</span></div>
              <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between'}}>
                <div style={{fontSize:22,fontWeight:600,letterSpacing:'-.02em'}}>{usage.toLocaleString()} <span style={{fontSize:14,color:'var(--ink-faint)',fontWeight:400}}>/ {totalAllowed.toLocaleString()} used</span></div>
                <div className="mono" style={{fontSize:12,color:pct>=90?'var(--danger)':pct>=70?'var(--warning)':'var(--success)'}}>{pct}%</div>
              </div>
              <div className="usage-bar" style={{marginTop:12}}>
                <div className={'usage-fill'+(pct<70?' ok':'')} style={{width:pct+'%'}}/>
              </div>
              {overage > 0 && (
                <div className="mono" style={{fontSize:11,color:'var(--ink-faint)',marginTop:6}}>
                  Base limit: {limit.toLocaleString()} · Overage allowance: +{overage.toLocaleString()}
                </div>
              )}
            </div>
            <div className="card card-pad">
              <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:8}}><I name="refresh" size={14}/><span style={{fontSize:11,fontFamily:'Geist Mono,monospace',letterSpacing:'.08em',textTransform:'uppercase',color:'var(--ink-faint)'}}>Resets</span></div>
              <div style={{fontSize:22,fontWeight:600,letterSpacing:'-.02em'}}>
                {sub?.usage_resets_at ? new Date(sub.usage_resets_at).toLocaleDateString(undefined,{month:'short',day:'numeric'}) : '—'}
              </div>
              <div style={{color:'var(--ink-dim)',fontSize:13,marginTop:4}}>
                {sub?.usage_resets_at ? new Date(sub.usage_resets_at).toLocaleDateString(undefined,{weekday:'long',year:'numeric',month:'long',day:'numeric'}) : 'No reset date'}
              </div>
              <div style={{marginTop:'auto',paddingTop:10}}>
                <button className="btn btn-ghost btn-sm" onClick={load} disabled={refreshing} style={{width:'100%'}}>
                  <I name="refresh" size={12}/>{refreshing?'Refreshing…':'Refresh data'}
                </button>
              </div>
            </div>
          </div>

          {status !== 'active' && (
            <div className="card card-pad" style={{marginBottom:24,borderColor:'rgba(248,113,113,.3)',background:'rgba(248,113,113,.06)'}}>
              <div style={{display:'flex',alignItems:'center',gap:10,fontSize:13,color:'var(--danger)'}}>
                <I name="warn" size={14}/>
                <span>Your subscription is <b>inactive</b>. Diagram instantiation and other services may be limited or unavailable.</span>
              </div>
            </div>
          )}

          {usage >= limit && status === 'active' && (
            <div className="card card-pad" style={{marginBottom:24,borderColor:usage>totalAllowed?'rgba(248,113,113,.3)':'rgba(251,191,36,.3)',background:usage>totalAllowed?'rgba(248,113,113,.06)':'rgba(251,191,36,.06)'}}>
              <div style={{display:'flex',alignItems:'center',gap:10,fontSize:13,color:usage>totalAllowed?'var(--danger)':'var(--warning)'}}>
                <I name="warn" size={14}/>
                <span>
                  {usage > totalAllowed
                    ? 'You have exceeded your monthly instantiation limit, including allowed overages. Diagram instantiation may be blocked.'
                    : 'You have reached your base monthly instantiation limit. Overage charges may apply.'}
                </span>
              </div>
            </div>
          )}
        </>
      )}

      <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',margin:'4px 0 12px'}}>
        <h3 style={{margin:0,fontSize:15,fontWeight:600,letterSpacing:'-.01em'}}>Available plans</h3>
        <p style={{margin:0,fontSize:12,color:'var(--ink-faint)',fontFamily:'Geist Mono,monospace'}}>billed monthly · USD</p>
      </div>
      <div className="plan-grid">
        {plans.map(p => {
          const isCurrent = p.id === currentPlanId || (p.name.toLowerCase() === currentPlanId);
          return (
            <div key={p.id} className={'plan'+(p.featured?' featured':'')+(isCurrent?' current':'')}>
              <div className="pname">{p.name}</div>
              <div className="pprice"><b>{p.price}</b><span>/ month</span></div>
              <div className="plimit">{p.limit}</div>
              <ul style={{listStyle:'none',padding:0,margin:'8px 0',display:'flex',flexDirection:'column',gap:8,flex:1}}>
                {p.features.map((f,i)=>(
                  <li key={i} style={{display:'flex',gap:8,fontSize:13,color:'var(--ink-dim)',alignItems:'flex-start'}}>
                    <span style={{color:'var(--accent)',marginTop:2}}><I name="check" size={13}/></span>{f}
                  </li>
                ))}
              </ul>
              {isCurrent
                ? <button className="btn btn-ghost" disabled style={{opacity:.6,cursor:'default'}}>Current plan</button>
                : <button className={p.featured?'btn btn-accent':'btn btn-ghost'} onClick={()=>{const msg='Contact sales to change your plan'; if(window.modalAlert)window.modalAlert(msg); else alert(msg);}}>
                    {p.featured?'Upgrade to Starter':'Switch plan'}
                  </button>
              }
            </div>
          );
        })}
      </div>
      <p style={{marginTop:18,fontSize:12,color:'var(--ink-faint)',fontFamily:'Geist Mono,monospace'}}>
        Need higher volume or custom pricing? <a style={{color:'var(--accent-2)',borderBottom:'1px dashed currentColor',cursor:'pointer'}} onClick={()=>{const msg='Contact sales@fluentra.io'; if(window.modalAlert)window.modalAlert(msg); else alert(msg);}}>Talk to sales →</a>
      </p>
    </>
  );
}
