/* ParallelOS Control Plane — Root, routing, live simulation */
const { useState:rS, useEffect:rE, useRef:rR } = React;

function resultsFor(d){
  const base=(d.tpl||'job');
  const map={
    comfyui:[{n:'renders_batch.zip',s:(120+d.gpuCount*64)+' MB'},{n:'grid_preview.png',s:'3.4 MB'}],
    vllm:[{n:'completions.jsonl',s:'8.2 MB'},{n:'eval_metrics.json',s:'22 KB'}],
    whisper:[{n:'transcripts.jsonl',s:'5.1 MB'},{n:'srt_files.zip',s:'2.3 MB'}],
    finetune:[{n:'adapter_model.safetensors',s:'412 MB'},{n:'training_loss.csv',s:'48 KB'}],
    embed:[{n:'embeddings.parquet',s:(80+d.gpuCount*40)+' MB'},{n:'index.faiss',s:'156 MB'}],
    blender:[{n:'frames_0001-0240.zip',s:(240+d.gpuCount*90)+' MB'},{n:'render.log',s:'64 KB'}],
  };
  return [...(map[base]||[{n:'output.tar',s:(60+d.gpuCount*40)+' MB'}]),{n:'run.log',s:(40+Math.floor(Math.random()*90))+' KB'}];
}

function AppInner(){
  const { state, update } = useApp();
  const toast=useToast();
  const [route,setRoute]=rS({view:'overview'});
  const notified=rR({});
  const go=(view,extra={})=>{ setRoute({view,...extra}); document.querySelector('.main')&&(document.querySelector('.main').scrollTop=0); };
  const openDep=(id)=>go('dep-detail',{depId:id});
  const openDev=(id)=>go('dev-detail',{devId:id});

  rE(()=>{
    const iv=setInterval(()=>{
      update(prev=>{
        let ch=false;
        const deployments=prev.deployments.map(d=>{
          if(d.status==='provisioning'){
            ch=true; const logs=[...d.logs]; const step=logs.length;
            if(step===1){ logs.push({t:'00:00:03',lv:'info',msg:'scheduling · matching GPU='+d.gpu+' region='+d.region}); return {...d,logs}; }
            if(step===2){ const node='node-'+uid(4)+' · '+d.region; logs.push({t:'00:00:06',lv:'ok',msg:'lease opened on '+node}); logs.push({t:'00:00:08',lv:'info',msg:'pulling image '+d.image}); return {...d,logs,node:node.replace(' · ',' · '),status:'running',progress:0.02}; }
            return d;
          }
          if(d.status==='running'){
            ch=true; let progress=Math.min(1,d.progress+(0.04+Math.random()*0.09));
            const unitsDone=Math.round(progress*d.units); const logs=[...d.logs];
            const tsec=Math.floor(progress*d.maxH*3600*0.6); const ts=new Date(tsec*1000).toISOString().substr(11,8);
            if(d.progress<0.12&&progress>=0.12) logs.push({t:ts,lv:'ok',msg:'image ready · container started'});
            else if(d.progress<0.3&&progress>=0.3) logs.push({t:ts,lv:'info',msg:'inputs mounted → /inputs · executing'});
            else if(d.progress<0.7&&progress>=0.7) logs.push({t:ts,lv:'info',msg:'processing units '+Math.round(progress*d.units-8)+'–'+unitsDone+' / '+d.units});
            else if(Math.random()<0.4&&progress<0.95) logs.push({t:ts,lv:'info',msg:'unit '+unitsDone+'/'+d.units+' ok · gpu '+(70+Math.floor(Math.random()*25))+'%'});
            if(progress>=1){
              const runH=d.maxH*0.6; const rate=(GPUS.find(g=>g.id===d.gpu)||GPUS[0]).rate;
              const cost=Math.min(+(rate*d.gpuCount*runH*1.05).toFixed(2), d.budget);
              logs.push({t:ts,lv:'ok',msg:'all units complete · merging outputs'});
              logs.push({t:ts,lv:'ok',msg:'results uploaded → ipfs://bafy'+uid(4)+'…'+uid(3)});
              logs.push({t:ts,lv:'ok',msg:'lease closed · exit code 0'});
              if(!notified.current[d.id]){ notified.current[d.id]=1; setTimeout(()=>toast({kind:'ok',msg:'Deployment complete',sub:d.name}),0); }
              return {...d,status:'completed',progress:1,unitsDone:d.units,logs,cost,exitCode:0,
                results:resultsFor(d),metrics:{gpu:84+Math.floor(Math.random()*12),vram:(d.gpu.includes('80')?'62.0/80':'21.4/24')+' GB',thr:(2+Math.random()*4).toFixed(1)+' u/s'}};
            }
            return {...d,progress,unitsDone,logs};
          }
          return d;
        });
        // deduct balance for newly completed
        let balance=prev.balance;
        deployments.forEach((d,i)=>{ if(d.status==='completed'&&prev.deployments[i]&&prev.deployments[i].status==='running'){ balance=+(balance-d.cost).toFixed(2); } });
        // device telemetry + earnings
        const devices=prev.devices.map(dv=>{
          if(dv.status==='offline') return dv;
          ch=true;
          const util=Math.max(4,Math.min(99,dv.util+(Math.random()*16-8)|0));
          const temp=Math.max(40,Math.min(86,dv.temp+(Math.random()*4-2)|0));
          const power=dv.gpu.includes('80')?260+util*1.4:90+util*2.6;
          return {...dv,util,temp,power:Math.round(power),earned:+(dv.earned+(dv.status==='hired'?0.6:0.15)+Math.random()*0.5).toFixed(2)};
        });
        return ch?{...prev,deployments,devices,balance}:prev;
      });
    },800);
    return ()=>clearInterval(iv);
  },[]);

  const dep=route.depId&&state.deployments.find(d=>d.id===route.depId);
  const dev=route.devId&&state.devices.find(d=>d.id===route.devId);
  const crumbs=(()=>{
    switch(route.view){
      case 'overview':return [{label:'Overview'}];
      case 'deployments':return [{label:'Deployments'}];
      case 'new':return [{label:'Deployments',go:()=>go('deployments')},{label:'New deployment'}];
      case 'dep-detail':return [{label:'Deployments',go:()=>go('deployments')},{label:dep?dep.name:'…'}];
      case 'devices':return [{label:'Devices'}];
      case 'dev-detail':return [{label:'Devices',go:()=>go('devices')},{label:dev?dev.name:'…'}];
      case 'connect':return [{label:'Devices',go:()=>go('devices')},{label:'Connect device'}];
      case 'earnings':return [{label:'Earnings'}];
      case 'marketplace':return [{label:'Marketplace'}];
      case 'billing':return [{label:'Billing'}];
      case 'settings':return [{label:'Settings'}];
      default:return [{label:'Overview'}];
    }
  })();
  const action=(()=>{
    if(route.view==='overview'||route.view==='deployments') return <button className="b b-pri b-sm" onClick={()=>go('new')}><Icon n="plus"/>New deployment</button>;
    if(route.view==='devices') return <button className="b b-pri b-sm" onClick={()=>go('connect')}><Icon n="plus"/>Connect device</button>;
    if(route.view==='marketplace') return <button className="b b-sec b-sm" onClick={()=>go('new')}><Icon n="terminal"/>Custom image</button>;
    return null;
  })();
  const sideView=({'dep-detail':'deployments','dev-detail':'devices','connect':'devices','new':'deployments'})[route.view]||route.view;

  return (
    <div className="shell">
      <Sidebar view={sideView} go={go}/>
      <div className="main">
        <Topbar crumbs={crumbs} action={action}/>
        {route.view==='overview' && <Overview go={go} openDep={openDep}/>}
        {route.view==='deployments' && <Deployments go={go} openDep={openDep}/>}
        {route.view==='new' && <NewDeployment go={go} openDep={openDep} prefill={route.prefill}/>}
        {route.view==='dep-detail' && <DeploymentDetail id={route.depId} go={go}/>}
        {route.view==='devices' && <Devices go={go} openDev={openDev}/>}
        {route.view==='dev-detail' && <DeviceDetail id={route.devId} go={go}/>}
        {route.view==='connect' && <ConnectDevice go={go}/>}
        {route.view==='earnings' && <Earnings/>}
        {route.view==='marketplace' && <Marketplace go={go}/>}
        {route.view==='billing' && <Billing/>}
        {route.view==='settings' && <Settings/>}
      </div>
    </div>
  );
}

function Root(){ const {state}=useApp(); return state.wallet?<AppInner/>:<LoginGate/>; }
function App(){ return <ToastProvider><AppProvider><Root/></AppProvider></ToastProvider>; }
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
