/* Bee Smart Operations Portal - vNext canon prototype: routing + drill state +
   live-data boot gate. SITES/DEVICES are fetched by window.loadFleet() (OurData.jsx);
   the app shows loading -> ready / empty / error while that resolves. */

function CenterPane({children}){
  return <div style={{minHeight:'100vh',display:'flex',alignItems:'center',justifyContent:'center',background:O.bg,fontFamily:O.sans,padding:24}}>
    <div style={{textAlign:'center',maxWidth:420}}>{children}</div>
  </div>;
}
function gateBtn(onClick,label){
  return <button onClick={onClick} className="clk" style={{display:'inline-flex',alignItems:'center',gap:8,background:O.azure,border:'none',borderRadius:9,padding:'9px 22px',cursor:'pointer',fontFamily:O.sans,fontWeight:700,fontSize:13,color:'#fff'}}>{label}</button>;
}
function LoadingScreen(){
  return <CenterPane>
    <div className="spin" style={{width:34,height:34,margin:'0 auto 16px',border:`3px solid ${O.line}`,borderTopColor:O.azure,borderRadius:'50%'}}/>
    <div style={{fontWeight:700,fontSize:15,color:O.fg1}}>Loading fleet data…</div>
    <div style={{fontSize:13,color:O.fg3,marginTop:4}}>Fetching live telemetry from the mesh.</div>
  </CenterPane>;
}
function ErrorScreen({detail,onRetry}){
  return <CenterPane>
    <div style={{color:O.crit,marginBottom:12,display:'flex',justifyContent:'center'}}><OIcon name="alert-triangle" size={34}/></div>
    <div style={{fontWeight:700,fontSize:15,color:O.fg1}}>Couldn't load fleet data</div>
    <div style={{fontSize:13,color:O.fg3,marginTop:4,marginBottom:18,wordBreak:'break-word'}}>{detail||'The live data service is unavailable.'}</div>
    {gateBtn(onRetry,'Retry')}
  </CenterPane>;
}
function EmptyScreen({onRefresh}){
  return <CenterPane>
    <div style={{color:O.fg3,marginBottom:12,display:'flex',justifyContent:'center'}}><OIcon name="inbox" size={34}/></div>
    <div style={{fontWeight:700,fontSize:15,color:O.fg1}}>No devices are reporting</div>
    <div style={{fontSize:13,color:O.fg3,marginTop:4,marginBottom:18}}>The fleet returned no devices right now.</div>
    {gateBtn(onRefresh,'Refresh')}
  </CenterPane>;
}

function OpsApp(){
  const [route,setRoute]=React.useState('overview');
  const [device,setDevice]=React.useState(null);
  const [grower,setGrower]=React.useState(null);
  const [deviceFilter,setDeviceFilter]=React.useState(null);
  const [hist,setHist]=React.useState([]); // navigation history for the global Back button
  const [phase,setPhase]=React.useState('loading'); // 'loading' | 'ready' | 'error'
  const [loadErr,setLoadErr]=React.useState(null);
  const [,setTick]=React.useState(0);
  const [lastUpdated,setLastUpdated]=React.useState(null); // ms of the last successful load (PM-167 indicator)
  const [refreshing,setRefreshing]=React.useState(false);
  const inflight=React.useRef(false);
  // Single refresh path: the manual button, post-edit onSaved, and the 30s auto-poll
  // all route here. In-flight guard so a slow fetch can't pile up; stamps lastUpdated.
  const refresh=React.useCallback(()=>{
    if(inflight.current) return;
    inflight.current=true; setRefreshing(true);
    window.loadFleet().then(()=>{ setLastUpdated(Date.now()); setTick(t=>t+1); })
      .catch(e=>console.error('[canon] refresh failed:',e))
      .finally(()=>{ inflight.current=false; setRefreshing(false); });
  },[]);
  const boot=React.useCallback(()=>{
    setPhase('loading'); setLoadErr(null);
    window.loadFleet().then(()=>{ setLastUpdated(Date.now()); setPhase('ready'); }).catch(e=>{ console.error('[canon] loadFleet failed:',e); setLoadErr(String(e&&e.message||e)); setPhase('error'); });
  },[]);
  React.useEffect(()=>{ boot(); },[boot]);
  // PM-167 near-real-time status: auto-poll /api/fleet-snapshot every 30s, paused
  // while the tab is hidden (Page Visibility API), with an immediate refresh on re-focus.
  React.useEffect(()=>{
    // 30s poll; also force a re-render each tick (even on a FAILED poll) so the
    // data-delayed state (>=10 min, below) can flip + raise its alert without a successful fetch.
    const id=setInterval(()=>{ if(!document.hidden){ refresh(); setTick(t=>t+1); } },30000);
    const onVis=()=>{ if(!document.hidden) refresh(); };
    document.addEventListener('visibilitychange',onVis);
    return ()=>{ clearInterval(id); document.removeEventListener('visibilitychange',onVis); };
  },[refresh]);
  const push=()=>setHist(h=>[...h,{route,device,grower,deviceFilter}]);
  const openDevice=d=>{ push(); setDevice(d); window.scrollTo(0,0); };
  const openGrower=id=>{ const ds=DEVICES.filter(x=>x.site===id); if(ds.length===1){ openDevice(ds[0]); return; } /* single-device (standalone/consumer) grower -> straight to the device drill-down */ push(); setGrower(id); setDevice(null); window.scrollTo(0,0); };
  const nav=r=>{ push(); setDevice(null); setGrower(null); setDeviceFilter(null); setRoute(r); window.scrollTo(0,0); };
  const openDevices=(filter)=>{ push(); setDeviceFilter(filter||null); setDevice(null); setGrower(null); setRoute('devices'); window.scrollTo(0,0); };
  const goBack=()=>{ if(hist.length){ const p=hist[hist.length-1]; setHist(hist.slice(0,-1)); setRoute(p.route); setDevice(p.device); setGrower(p.grower); setDeviceFilter(p.deviceFilter); } else { setDevice(null); setGrower(null); setDeviceFilter(null); setRoute('overview'); } window.scrollTo(0,0); };

  if(phase==='loading') return <LoadingScreen/>;
  if(phase==='error') return <ErrorScreen detail={loadErr} onRetry={boot}/>;
  if(!DEVICES || DEVICES.length===0) return <EmptyScreen onRefresh={boot}/>;

  // Portal data-delayed: >=10 min since the last successful fetch -> the topbar LiveIndicator
  // goes green->red AND a client-side Critical alert is raised (bell badge + Alerts page).
  // Clears automatically when a poll succeeds (lastUpdated resets). Per-browser/session.
  const dataDelayed = lastUpdated!=null && (Date.now()-lastUpdated)>=600000;
  const extraAlerts = dataDelayed ? [{ id:'portal-data-delayed', severity:'critical', status:'active', title:'Portal data delayed', detail:'No fresh fleet data received for over 10 minutes -- the portal may have lost its connection to the backend; readings shown may be out of date.', site:null, node:null }] : [];

  let body;
  if(device) body=<DeviceDetail device={device} onDevice={openDevice}/>;
  else if(grower) body=<GrowerDetail siteId={grower} onDevice={openDevice} onDevices={openDevices} onAlerts={()=>nav('alerts')} onSaved={refresh}/>;
  else if(route==='growers') body=<Growers onGrower={openGrower} onSensors={()=>openDevices()} onSaved={refresh}/>;
  else if(route==='overview') body=<Overview onNav={nav} onDevices={openDevices}/>;
  else if(route==='devices') body=<Devices onDevice={openDevice} initial={deviceFilter}/>;
  else if(route==='topology') body=<Topology onDevice={openDevice} onGrower={openGrower}/>;
  else if(route==='alerts') body=<Alerts onDevice={openDevice} extra={extraAlerts}/>;
  else body=<Integrations/>;

  // Back button: matches the Add Device (primBtn) vertical size - 8px pad + 1px border = 9px, 13px font; keeps its own white/outlined coloring.
  const backBtn=<button onClick={goBack} className="clk" style={{display:'inline-flex',alignItems:'center',gap:7,background:'#fff',border:`1px solid ${O.lineS}`,borderRadius:9,padding:'8px 20px',cursor:'pointer',fontFamily:O.sans,fontWeight:700,fontSize:13,color:O.fg1,marginBottom:18}}><OIcon name="arrow-left" size={15}/>Back</button>;
  const showBack = !(route==='overview' && !device && !grower); // no Back on the Overview landing page
  return <Shell route={route} onNav={nav} alertCount={activeAlerts.length+extraAlerts.length} siteFilter="all" onSite={id=>{ if(id&&id!=='all') openGrower(id); }} lastUpdated={lastUpdated} refreshing={refreshing} onRefresh={refresh}>{showBack?backBtn:null}{body}</Shell>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<OpsApp/>);
