/* Bee Smart Operations Portal — shared kit: tokens, Icon, app shell, primitives.
   ADD-TO the Bee Smart design system. Loaded after React + Babel + Lucide. */

const O = {
  azure:'#1F6FE0', azure6:'#1657BE', azure7:'#14479B', azure4:'#3D82E6', azure1:'#CFE2FB', azure05:'#EAF2FD',
  leaf:'#1B8E54', leaf4:'#2FA968', leaf1:'#C7EBD3', leaf05:'#E7F6EC',
  cyan:'#15B5DE', teal:'#0FA89A', gold:'#C49A22', goldDeep:'#8F6E14',
  // status / severity
  ok:'#1B8E54', okBg:'#E7F6EC', warn:'#C77D12', warnBg:'#FBEFD6', crit:'#C8402B', critBg:'#FBE3DD',
  info:'#1F6FE0', infoBg:'#EAF2FD', idle:'#6E8298', idleBg:'#EDF1F6',
  // neutrals
  ink:'#16202B', fg1:'#16202B', fg2:'#3A4B5E', fg3:'#6E8298', faint:'#9DAEC2',
  bg:'#F6F8FB', raised:'#FFFFFF', sunken:'#EDF1F6', line:'#DDE4EE', lineS:'#C4D0DE',
  navy:'#0E1C33', navy2:'#16202B',
  sans:"'Hanken Grotesk', system-ui, sans-serif", disp:"'Archivo', system-ui, sans-serif", mono:"'IBM Plex Mono', monospace",
};
const STATUS = {
  online:   { c:O.ok,       bg:O.okBg,    label:'Online' },
  offline:  { c:O.crit,     bg:O.critBg,  label:'Offline' },
  stale:    { c:O.warn,     bg:O.warnBg,  label:'Stale' },    // s356 user ruling: STALE = late data (device reporting late)
  degraded: { c:O.goldDeep, bg:'#FAF1D8', label:'Degraded' }, // s356 user ruling: DEGRADED = network or platform performance (no device in this state yet)
};
const SEV = {
  critical:{ c:O.crit, bg:O.critBg, label:'Critical', icon:'alert-octagon' },
  warning: { c:O.warn, bg:O.warnBg, label:'Warning',  icon:'alert-triangle' },
  info:    { c:O.info, bg:O.infoBg, label:'Info',     icon:'info' },
};
const KIND = { gateway:{icon:'router', label:'Gateway'}, router:{icon:'share-2', label:'Router'}, sensor:{icon:'radio', label:'Sensor'} };

function OIcon({ name, size=18, color, style }){
  const ref = React.useRef(null);
  React.useLayoutEffect(()=>{ const n=ref.current; if(!n||!window.lucide) return; n.innerHTML=''; const i=document.createElement('i'); i.setAttribute('data-lucide',name); n.appendChild(i); window.lucide.createIcons(); const s=n.querySelector('svg'); if(s){s.setAttribute('width',size);s.setAttribute('height',size);} });
  return <span ref={ref} style={{display:'inline-flex',alignItems:'center',justifyContent:'center',width:size,height:size,color:color||'currentColor',...style}} />;
}

function StatusPill({ status, sm }){
  const s = STATUS[status]||STATUS.stale;
  return <span style={{display:'inline-flex',alignItems:'center',justifyContent:'center',background:s.bg,color:s.c,fontFamily:O.sans,fontWeight:700,fontSize:sm?11:12,padding:sm?'3px 10px':'4px 13px',borderRadius:999,whiteSpace:'nowrap'}}>{s.label}</span>;
}

function Battery({ pct }){
  const c = pct<=25?O.crit:pct<50?O.warn:O.fg2;
  return <span style={{display:'inline-flex',alignItems:'center',gap:6,fontFamily:O.mono,fontSize:12.5,fontWeight:600,color:c}}>
    <span style={{position:'relative',width:24,height:12,border:`1.5px solid ${c}`,borderRadius:3}}>
      <span style={{position:'absolute',right:-3.5,top:3,width:2,height:4,background:c,borderRadius:1}} />
      <span style={{position:'absolute',left:1.5,top:1.5,bottom:1.5,width:`calc(${Math.max(4,pct)}% - 3px)`,background:c,borderRadius:1}} />
    </span>{pct}%
  </span>;
}

// Sparkline (area)
function Spark({ data, color=O.azure, w=120, h=34, fill=true }){
  const max=Math.max(...data), min=Math.min(...data), rng=(max-min)||1;
  const pts=data.map((v,i)=>[i/(data.length-1)*w, h-((v-min)/rng)*(h-4)-2]);
  const d=pts.map((p,i)=>(i?'L':'M')+p[0].toFixed(1)+' '+p[1].toFixed(1)).join(' ');
  const area=d+` L ${w} ${h} L 0 ${h} Z`;
  const gid='sg'+Math.random().toString(36).slice(2,7);
  return <svg width="100%" height={h} viewBox={`0 0 ${w} ${h}`} style={{display:'block',overflow:'visible'}} preserveAspectRatio="none">
    <defs><linearGradient id={gid} x1="0" y1="0" x2="0" y2="1"><stop offset="0" stopColor={color} stopOpacity="0.22"/><stop offset="1" stopColor={color} stopOpacity="0"/></linearGradient></defs>
    {fill && <path d={area} fill={`url(#${gid})`} />}
    <path d={d} fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
  </svg>;
}

// Donut for fleet composition / online ratio
function Donut({ value, total, color=O.leaf, size=92, label }){
  const r=size/2-8, C=2*Math.PI*r, pct=total?value/total:0;
  return <div style={{position:'relative',width:size,height:size}}>
    <svg width={size} height={size}><g transform={`rotate(-90 ${size/2} ${size/2})`}>
      <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={O.sunken} strokeWidth="8" />
      <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={color} strokeWidth="8" strokeLinecap="round" strokeDasharray={`${C*pct} ${C}`} />
    </g></svg>
    <div style={{position:'absolute',inset:0,display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center'}}>
      <span style={{fontFamily:O.disp,fontWeight:800,fontSize:size>80?20:16,color:O.fg1,lineHeight:1}}>{Math.round(pct*100)}%</span>
      {label&&<span style={{fontFamily:O.mono,fontSize:9,color:O.fg3,marginTop:2,textTransform:'uppercase',letterSpacing:'.06em'}}>{label}</span>}
    </div>
  </div>;
}

function Bars({ data, color=O.azure, h=56 }){
  const max=Math.max(...data)||1;
  return <div style={{display:'flex',alignItems:'flex-end',gap:2,height:h}}>
    {data.map((v,i)=><div key={i} style={{flex:1,height:`${Math.max(3,v/max*100)}%`,background:i===data.length-1?color:color+'66',borderRadius:'2px 2px 0 0'}} />)}
  </div>;
}

function Card({ children, style, pad=18, onClick, className }){
  const float = !!className && className.split(' ').includes('clk'); // .clk owns the shadow; drop the inline one so the :hover lift works
  return <div onClick={onClick} className={className} style={{background:O.raised,border:`1px solid ${O.line}`,borderRadius:14,...(float?{}:{boxShadow:'0 1px 2px rgba(22,32,43,0.04)'}),padding:pad,...style}}>{children}</div>;
}
function CardHead({ title, sub, right, icon }){
  return <div style={{display:'flex',alignItems:'flex-start',justifyContent:'space-between',marginBottom:14,gap:12}}>
    <div style={{display:'flex',gap:10,alignItems:'center'}}>
      {icon&&<span style={{width:30,height:30,borderRadius:8,background:O.azure05,color:O.azure,display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}}><OIcon name={icon} size={16}/></span>}
      <div><div style={{fontFamily:O.sans,fontWeight:700,fontSize:15,color:O.fg1}}>{title}</div>{sub&&<div style={{fontFamily:O.sans,fontSize:12.5,color:O.fg3,marginTop:1}}>{sub}</div>}</div>
    </div>
    {right}
  </div>;
}

// Live freshness indicator (PM-167): "Live . updated Ns ago" + manual refresh.
// Self-contained 1s tick so only this pill re-renders (no app-wide flicker). The
// 30s auto-poll lives in OpsApp; this shows the age of the last successful load.
function fmtAgoSec(s){ if(s<5) return 'just now'; if(s<60) return s+'s ago'; const m=Math.floor(s/60); if(m<60) return m+'m ago'; const h=Math.floor(m/60); return h+'h ago'; }
function LiveIndicator({ lastUpdated, refreshing, onRefresh }){
  const [,tick]=React.useState(0);
  React.useEffect(()=>{ const id=setInterval(()=>tick(t=>t+1),1000); return ()=>clearInterval(id); },[]);
  const ageSec = lastUpdated!=null ? Math.max(0,Math.floor((Date.now()-lastUpdated)/1000)) : null;
  const stale = ageSec!=null && ageSec>=600; // 10 min since the last successful fetch = data delayed (green->red; raises a Critical alert)
  const dot = refreshing ? O.azure : stale ? O.crit : O.leaf;
  const txt = refreshing ? 'Updating...' : ageSec==null ? 'Connecting...' : (stale?'Delayed':'Live')+' . updated '+fmtAgoSec(ageSec);
  return <div title="Fleet data auto-refreshes every 30s" style={{display:'flex',alignItems:'center',gap:8,fontFamily:O.mono,fontSize:11.5,fontWeight:600,color:stale?O.crit:O.fg3,whiteSpace:'nowrap'}}>
    <span style={{width:8,height:8,borderRadius:'50%',background:dot,boxShadow:`0 0 0 3px ${dot}22`,flexShrink:0}} />
    <span className="ops-live-txt">{txt}</span>
    <button onClick={onRefresh} title="Refresh now" aria-label="Refresh now" className="clk" style={{background:'#fff',border:`1px solid ${O.line}`,borderRadius:9,width:38,height:38,cursor:'pointer',color:O.fg2,display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}}>
      <OIcon name="refresh-cw" size={16} style={refreshing?{animation:'spin .8s linear infinite'}:undefined}/>
    </button>
  </div>;
}

// ---- App shell: collapsible sidebar + topbar ----
const NAV = [
  { id:'overview', label:'Overview', icon:'layout-dashboard' },
  { id:'growers', label:'Growers', icon:'sprout' },
  { id:'devices', label:'Devices', icon:'cpu' },
  { id:'topology', label:'Network', icon:'share-2' },
  { id:'integrations', label:'Integrations', icon:'plug' },
  { id:'alerts', label:'Alerts', icon:'bell' },
];

function Shell({ route, onNav, alertCount, children, siteFilter, onSite, lastUpdated, refreshing, onRefresh }){
  const [open,setOpen]=React.useState(false); // mobile drawer
  return <div style={{minHeight:'100vh',background:O.bg,fontFamily:O.sans,display:'flex'}}>
    {/* sidebar */}
    <aside className="ops-side" data-open={open?'1':'0'} style={{width:232,flexShrink:0,background:O.navy,color:'#fff',display:'flex',flexDirection:'column',position:'sticky',top:0,height:'100vh'}}>
      {/* vNext delta: logo sized to the BEE SMART / OPERATIONS wordmark block height (38px, s356 user direction) + wordmark (OPERATIONS 17; BEE SMART 20). */}
      <div onClick={()=>onNav('overview')} role="button" tabIndex={0} title="Go to Overview" style={{padding:'18px 18px 14px',display:'flex',alignItems:'center',gap:12,cursor:'pointer'}}>
        <img src="assets/bee-icon-final.svg?v=13" alt="" style={{width:38}} />
        <div style={{lineHeight:1.0,flex:1,textAlign:'center',transform:'translateX(-4px)'}}>
          <div style={{fontFamily:O.disp,fontWeight:800,fontSize:20,letterSpacing:'-0.01em',textTransform:'uppercase',color:'#fff',whiteSpace:'nowrap'}}><span style={{color:'#4A90F2'}}>Bee Smart</span></div>
          <div style={{fontFamily:O.disp,fontWeight:800,fontSize:17,letterSpacing:'-0.005em',textTransform:'uppercase',color:'#fff',marginTop:1,whiteSpace:'nowrap'}}>Operations</div>
        </div>
      </div>
      {/* s358: platform (infrastructure) status -- distinct from the device-fleet pill on Overview.
          Green unless a platform component is degraded (amber) or down (red); clickable -> Integrations. */}
      {(()=>{ const tone={ok:O.leaf,warn:O.goldDeep,crit:O.crit}[PLATFORM.tone]||O.leaf;
        return <div onClick={()=>onNav('integrations')} role="button" tabIndex={0} title="Platform status - open Integrations" style={{margin:'0 14px 6px',padding:'12px 14px',borderRadius:10,background:'rgba(255,255,255,0.05)',borderBottom:'1px solid rgba(255,255,255,0.08)',textAlign:'center',cursor:'pointer'}}>
        <div style={{fontFamily:O.mono,fontSize:14.5,fontWeight:700,color:'#AEC4E6',letterSpacing:'.01em'}}>portal.tbdcloud.com</div>
        <div style={{fontFamily:O.mono,fontSize:12.5,fontWeight:700,color:'#fff',marginTop:6,display:'flex',alignItems:'center',justifyContent:'center',gap:7,letterSpacing:'.02em'}}><span style={{width:9,height:9,borderRadius:'50%',background:tone,boxShadow:`0 0 0 3px ${tone}33`}} />{PLATFORM.label}</div>
      </div>; })()}
      <nav style={{padding:'14px 12px',display:'flex',flexDirection:'column',gap:3,flex:1}}>
        {NAV.map(n=>{ const on=route===n.id; return (
          <button key={n.id} onClick={()=>{onNav(n.id);setOpen(false);}} style={{display:'flex',alignItems:'center',gap:11,padding:'10px 12px',borderRadius:9,border:'none',cursor:'pointer',textAlign:'left',
            background:on?O.azure:'transparent',color:on?'#fff':'#AEC4E6',fontFamily:O.sans,fontWeight:on?700:600,fontSize:14}}>
            <OIcon name={n.icon} size={18}/>{n.label}
            {n.id==='alerts'&&alertCount>0&&<span style={{marginLeft:'auto',background:on?'rgba(255,255,255,0.25)':O.crit,color:'#fff',fontFamily:O.mono,fontSize:10.5,fontWeight:700,padding:'1px 7px',borderRadius:999}}>{alertCount}</span>}
          </button>
        );})}
      </nav>
    </aside>
    {open&&<div onClick={()=>setOpen(false)} className="ops-scrim" style={{position:'fixed',inset:0,background:'rgba(14,28,51,0.4)',zIndex:40}} />}

    <div style={{flex:1,minWidth:0,display:'flex',flexDirection:'column'}}>
      {/* topbar */}
      <header style={{position:'sticky',top:0,zIndex:30,background:'rgba(246,248,251,0.85)',backdropFilter:'blur(10px)',WebkitBackdropFilter:'blur(10px)',borderBottom:`1px solid ${O.line}`,padding:'12px 24px',display:'flex',alignItems:'center',gap:16}}>
        <button className="ops-burger" onClick={()=>setOpen(true)} style={{display:'none',background:'none',border:'none',cursor:'pointer',color:O.fg1}}><OIcon name="menu" size={22}/></button>
        <div style={{position:'relative',flex:1,maxWidth:380}}>
          <span style={{position:'absolute',left:11,top:'50%',transform:'translateY(-50%)',color:O.faint}}><OIcon name="search" size={16}/></span>
          <input placeholder="Search devices, sites, alerts…" style={{width:'100%',boxSizing:'border-box',padding:'9px 12px 9px 34px',borderRadius:9,border:`1px solid ${O.line}`,background:'#fff',fontFamily:O.sans,fontSize:13.5,color:O.fg1,outline:'none'}} />
        </div>
        <select value={siteFilter} onChange={e=>onSite(e.target.value)} style={{padding:'9px 12px',borderRadius:9,border:`1px solid ${O.line}`,background:'#fff',fontFamily:O.sans,fontWeight:600,fontSize:13,color:O.fg1,outline:'none'}}>
          <option value="all">All sites</option>
          {SITES.map(s=><option key={s.id} value={s.id}>{s.name}</option>)}
        </select>
        <button style={{position:'relative',background:'#fff',border:`1px solid ${O.line}`,borderRadius:9,width:38,height:38,cursor:'pointer',color:O.fg2,display:'flex',alignItems:'center',justifyContent:'center'}}>
          <OIcon name="bell" size={18}/>
          {alertCount>0&&<span style={{position:'absolute',top:-4,right:-4,background:O.crit,color:'#fff',fontFamily:O.mono,fontSize:9.5,fontWeight:700,minWidth:16,height:16,borderRadius:999,display:'flex',alignItems:'center',justifyContent:'center',padding:'0 3px'}}>{alertCount}</span>}
        </button>
        <div style={{display:'flex',alignItems:'center',gap:11,marginLeft:'auto'}}>
          {onRefresh && <LiveIndicator lastUpdated={lastUpdated} refreshing={refreshing} onRefresh={onRefresh} />}
          <div style={{width:34,height:34,borderRadius:'50%',background:O.azure,color:'#fff',display:'flex',alignItems:'center',justifyContent:'center',fontFamily:O.disp,fontWeight:800,fontSize:14}}>OT</div>
          <div className="ops-user" style={{lineHeight:1.1}}><div style={{fontFamily:O.sans,fontWeight:700,fontSize:13,color:O.fg1}}>Ops Team</div><div style={{fontFamily:O.sans,fontSize:11.5,color:O.fg3}}>NetOps · admin</div></div>
        </div>
      </header>
      {/* vNext canon ribbon: prototype-only chrome, hidden when served as production (PROD_PORTAL). */}
      {!PROD_PORTAL && <div style={{background:O.navy,color:'#fff',padding:'7px 24px',display:'flex',alignItems:'center',gap:10,flexWrap:'wrap',fontFamily:O.sans,fontSize:12}}>
        <span style={{fontFamily:O.mono,fontWeight:700,fontSize:10.5,letterSpacing:'.08em',textTransform:'uppercase',background:O.gold,color:O.navy,padding:'2px 8px',borderRadius:999}}>Prototype - vNext Canon</span>
        <span style={{color:'#AEC4E6'}}>Planned next version of <b style={{color:'#fff'}}>portal.tbdcloud.com</b> - the sign-off surface. Live fleet via /api/fleet-snapshot, auto-refreshed every 30s. Not the production portal.</span>
      </div>}
      <main style={{padding:'22px 24px 48px',flex:1}}>{children}</main>
    </div>
  </div>;
}

function PageTitle({ title, sub, right }){
  return <div style={{display:'flex',alignItems:'flex-end',justifyContent:'space-between',gap:16,marginBottom:20,flexWrap:'wrap'}}>
    <div><h1 style={{fontFamily:O.disp,fontWeight:800,fontSize:26,letterSpacing:'-0.02em',textTransform:'uppercase',color:O.fg1,margin:0}}>{title}</h1>{sub&&<p style={{fontFamily:O.sans,fontSize:14.5,color:O.fg3,margin:'4px 0 0'}}>{sub}</p>}</div>
    {right}
  </div>;
}

Object.assign(window,{ O, STATUS, SEV, KIND, OIcon, StatusPill, Battery, Spark, Donut, Bars, Card, CardHead, Shell, PageTitle, NAV });
