/* ParallelOS Control Plane — store, data model, icons */
const { useState, useEffect, useRef, useCallback, createContext, useContext } = React;
const LS = 'parallelos_cp_v1';

/* ---------- icon set (line icons, no emoji) ---------- */
const ICONS = {
  grid:'M3 3h7v7H3zM14 3h7v7h-7zM14 14h7v7h-7zM3 14h7v7H3z',
  layers:'M12 2 2 7l10 5 10-5zM2 12l10 5 10-5M2 17l10 5 10-5',
  cube:'M21 16V8a2 2 0 0 0-1-1.7l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.7l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16zM3.3 7 12 12l8.7-5M12 22V12',
  plus:'M12 5v14M5 12h14',
  cpu:'M9 2v3M15 2v3M9 19v3M15 19v3M2 9h3M2 15h3M19 9h3M19 15h3M5 5h14v14H5zM9 9h6v6H9z',
  server:'M3 4h18v6H3zM3 14h18v6H3zM7 7h.01M7 17h.01',
  wallet:'M21 12V7H5a2 2 0 0 1 0-4h14v4M3 5v14a2 2 0 0 0 2 2h16v-5M16 12a2 2 0 0 0 0 4h5v-4z',
  activity:'M22 12h-4l-3 9L9 3l-3 9H2',
  doc:'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8zM14 2v6h6M16 13H8M16 17H8M10 9H8',
  download:'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3',
  terminal:'M4 17l6-6-6-6M12 19h8',
  zap:'M13 2 3 14h9l-1 8 10-12h-9z',
  film:'M2 2h20v20H2zM2 7h20M2 17h20M7 2v20M17 2v20',
  brain:'M12 5a3 3 0 0 0-3 3 3 3 0 0 0-2 5 3 3 0 0 0 3 4 3 3 0 0 0 5 0 3 3 0 0 0 3-4 3 3 0 0 0-2-5 3 3 0 0 0-4-3z',
  db:'M12 2c4.4 0 8 1.3 8 3s-3.6 3-8 3-8-1.3-8-3 3.6-3 8-3zM4 5v14c0 1.7 3.6 3 8 3s8-1.3 8-3V5',
  chart:'M3 3v18h18M7 14l3-3 3 3 5-6',
  check:'M20 6 9 17l-5-5',
  copy:'M9 9h11v11H9zM5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1',
  x:'M18 6 6 18M6 6l12 12',
  gear:'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM19.4 15a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-2.7 1.1V21a2 2 0 1 1-4 0v-.1a1.6 1.6 0 0 0-2.7-1.1l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0-1.1-2.7H3a2 2 0 1 1 0-4h.1a1.6 1.6 0 0 0 1.1-2.7l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.6 1.6 0 0 0 1.8.3 1.6 1.6 0 0 0 .9-1.4V3a2 2 0 1 1 4 0v.1a1.6 1.6 0 0 0 2.7 1.1l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0 1.1 2.7H21a2 2 0 1 1 0 4h-.1a1.6 1.6 0 0 0-1.5.9z',
  thermo:'M14 14.8V4a2 2 0 0 0-4 0v10.8a4 4 0 1 0 4 0z',
  bolt:'M13 2 3 14h9l-1 8 10-12h-9z',
  globe:'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zM2 12h20M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20z',
  clock:'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zM12 6v6l4 2',
  coins:'M12 8c4.4 0 8-1.3 8-3s-3.6-3-8-3-8 1.3-8 3 3.6 3 8 3zM4 5v6c0 1.7 3.6 3 8 3s8-1.3 8-3V5M4 11v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6',
  arrowR:'M5 12h14M13 6l6 6-6 6',
  search:'M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM21 21l-4.3-4.3',
  key:'M15.5 7.5a4.5 4.5 0 1 1-6 4.2L3 18.2V21h2.8l.9-.9H8v-1.8h1.8l1.5-1.5a4.5 4.5 0 0 0 4.2-9.3zM17 7h.01',
  card:'M3 6h18v12H3zM3 10h18',
  shield:'M12 2 4 5v6c0 5 3.5 8.5 8 10 4.5-1.5 8-5 8-10V5z',
  store:'M3 9l1-5h16l1 5M4 9v11h16V9M4 9h16M9 20v-6h6v6',
  refresh:'M3 12a9 9 0 0 1 15-6.7L21 8M21 3v5h-5M21 12a9 9 0 0 1-15 6.7L3 16M3 21v-5h5',
  link:'M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1',
  trash:'M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6M10 11v6M14 11v6',
  filter:'M22 3H2l8 9.5V19l4 2v-8.5z',
  star:'M12 2l3 6.5 7 .9-5 4.8 1.3 7L12 18l-6.3 3.2L7 14.2 2 9.4l7-.9z',
  play:'M6 4l14 8-14 8z',
};
function Icon({n,style}){ return (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" style={style}><path d={ICONS[n]||''}/></svg>); }

/* ---------- catalogs ---------- */
const TEMPLATES = [
  { id:'comfyui', name:'ComfyUI · SDXL', icon:'film', image:'docker.io/parallelos/comfyui:2.4', cmd:'python main.py --listen', cat:'Image Gen', minGpu:'RTX 4090', price:0.34, desc:'Node-based Stable Diffusion XL pipeline for batch image generation.', pulls:'48.2k', rating:4.9 },
  { id:'flux', name:'FLUX.1 dev', icon:'film', image:'docker.io/parallelos/flux:1.0', cmd:'python serve.py --model flux-dev', cat:'Image Gen', minGpu:'A100 80GB', price:0.62, desc:'High-fidelity text-to-image with the FLUX.1 diffusion transformer.', pulls:'22.7k', rating:4.8 },
  { id:'vllm', name:'vLLM · Llama-3-70B', icon:'brain', image:'docker.io/parallelos/vllm:0.5', cmd:'python -m vllm.entrypoints.api_server --model meta-llama/Llama-3-70B', cat:'LLM Serving', minGpu:'A100 80GB', price:1.2, desc:'OpenAI-compatible API server with paged-attention throughput.', pulls:'61.4k', rating:4.9 },
  { id:'tgi', name:'TGI · Mixtral 8x7B', icon:'brain', image:'docker.io/parallelos/tgi:2.0', cmd:'text-generation-launcher --model-id mistralai/Mixtral-8x7B', cat:'LLM Serving', minGpu:'A100 80GB', price:0.9, desc:'Text Generation Inference server for sparse MoE models.', pulls:'18.9k', rating:4.7 },
  { id:'whisper', name:'Whisper v3 · Batch', icon:'activity', image:'docker.io/parallelos/whisper:3.1', cmd:'python transcribe.py --in /inputs --out /outputs', cat:'Speech', minGpu:'RTX 3090', price:0.09, desc:'Large-v3 speech-to-text over a folder of audio files.', pulls:'33.1k', rating:4.8 },
  { id:'xtts', name:'XTTS v2 · Voice', icon:'activity', image:'docker.io/parallelos/xtts:2.0', cmd:'python tts_server.py', cat:'Speech', minGpu:'RTX 3090', price:0.11, desc:'Multilingual voice cloning & text-to-speech synthesis.', pulls:'9.4k', rating:4.6 },
  { id:'finetune', name:'LoRA Fine-tune', icon:'brain', image:'docker.io/parallelos/axolotl:0.4', cmd:'accelerate launch train.py config.yaml', cat:'Training', minGpu:'A100 80GB', price:1.6, desc:'Axolotl LoRA / QLoRA fine-tuning with DeepSpeed.', pulls:'27.5k', rating:4.9 },
  { id:'nanogpt', name:'PyTorch DDP Train', icon:'brain', image:'docker.io/parallelos/pytorch:2.3', cmd:'torchrun --nproc_per_node=N train.py', cat:'Training', minGpu:'H100 80GB', price:2.1, desc:'Multi-GPU distributed data-parallel training boilerplate.', pulls:'14.8k', rating:4.7 },
  { id:'embed', name:'BGE Embeddings', icon:'db', image:'docker.io/parallelos/tei:1.2', cmd:'text-embeddings-router --model-id BAAI/bge-large', cat:'Embeddings', minGpu:'RTX 4090', price:0.06, desc:'High-throughput embedding inference for RAG pipelines.', pulls:'40.3k', rating:4.8 },
  { id:'blender', name:'Blender Cycles', icon:'film', image:'docker.io/parallelos/blender:4.1', cmd:'blender -b scene.blend -E CYCLES -f 1-240', cat:'Rendering', minGpu:'RTX 4090', price:0.28, desc:'Headless Cycles frame rendering for animation jobs.', pulls:'12.6k', rating:4.7 },
  { id:'svd', name:'Stable Video Diffusion', icon:'film', image:'docker.io/parallelos/svd:1.1', cmd:'python generate.py --frames 25', cat:'Video', minGpu:'A100 80GB', price:0.74, desc:'Image-to-video generation with Stable Video Diffusion.', pulls:'8.1k', rating:4.5 },
  { id:'yolo', name:'YOLOv8 · Detection', icon:'cube', image:'docker.io/parallelos/yolo:8.2', cmd:'yolo predict source=/inputs', cat:'Vision', minGpu:'RTX 3090', price:0.05, desc:'Real-time object detection & segmentation over image sets.', pulls:'19.2k', rating:4.6 },
];
const CATEGORIES=['All','Image Gen','LLM Serving','Speech','Training','Embeddings','Rendering','Video','Vision'];
const GPUS = [
  { id:'RTX 3090', vram:24, rate:0.22 },
  { id:'RTX 4080', vram:16, rate:0.28 },
  { id:'RTX 4090', vram:24, rate:0.34 },
  { id:'L40S', vram:48, rate:0.78 },
  { id:'A100 80GB', vram:80, rate:1.10 },
  { id:'H100 80GB', vram:80, rate:2.40 },
];
const REGIONS = ['us-east','us-west','eu-central','eu-west','ap-southeast'];

/* ---------- seed ---------- */
const SEED_DEVICES = [
  { id:'8f3a-2c91', name:'aurora-01', gpu:'RTX 4090', vram:24, region:'us-east', ip:'24.118.40.7', status:'hired', util:87, temp:71, power:340, uptimeH:342, earned:1284.5, jobs:942, rep:98.7 },
  { id:'1c9e-77b2', name:'aurora-02', gpu:'RTX 3090', vram:24, region:'us-east', ip:'24.118.40.8', status:'online', util:12, temp:48, power:120, uptimeH:208, earned:842.1, jobs:610, rep:96.2 },
  { id:'b22d-4f0a', name:'edge-rig', gpu:'RTX 4080', vram:16, region:'eu-central', ip:'81.94.12.55', status:'offline', util:0, temp:34, power:0, uptimeH:0, earned:317.9, jobs:230, rep:88.4 },
];
function seedLogs(){return[
  {t:'00:00:01',lv:'info',msg:'manifest accepted · validating spec'},
  {t:'00:00:03',lv:'info',msg:'scheduling · matching GPU=RTX 4090 region=us-east'},
  {t:'00:00:06',lv:'ok',msg:'lease opened on node aurora-01 (8f3a-2c91)'},
  {t:'00:00:09',lv:'info',msg:'pulling image docker.io/parallelos/comfyui:2.4 (3.2 GB)'},
  {t:'00:00:48',lv:'ok',msg:'image ready · starting container'},
  {t:'00:00:51',lv:'info',msg:'mounting inputs → /inputs (128 items)'},
];}
const SEED_DEPLOYMENTS = [
  { id:'dep_7a3f9c', name:'sdxl-product-shots', tpl:'comfyui', image:'docker.io/parallelos/comfyui:2.4', cmd:'python main.py --batch /inputs', gpu:'RTX 4090', gpuCount:2, region:'us-east', maxH:2, budget:40, env:[{k:'STEPS',v:'30'},{k:'CFG',v:'7.5'}], inputs:'ipfs://bafy…d41/refs.zip',
    status:'completed', progress:1, unitsDone:128, units:128, createdAt:Date.now()-7200e3, node:'aurora-01 · 8f3a-2c91', cost:12.42, exitCode:0,
    results:[{n:'renders_0001-0128.zip',s:'248 MB'},{n:'manifest.json',s:'14 KB'},{n:'run.log',s:'82 KB'}], metrics:{gpu:91,vram:'21.4/24 GB',thr:'4.2 img/s'} },
  { id:'dep_2b8e11', name:'whisper-podcast-batch', tpl:'whisper', image:'docker.io/parallelos/whisper:3.1', cmd:'python transcribe.py --in /inputs', gpu:'RTX 3090', gpuCount:1, region:'us-east', maxH:3, budget:18, env:[{k:'LANG',v:'auto'}], inputs:'ipfs://bafy…9ab/audio/',
    status:'completed', progress:1, unitsDone:64, units:64, createdAt:Date.now()-3600e3, node:'aurora-02 · 1c9e-77b2', cost:4.83, exitCode:0,
    results:[{n:'transcripts.jsonl',s:'5.1 MB'},{n:'srt_files.zip',s:'2.3 MB'}], metrics:{gpu:78,vram:'9.1/24 GB',thr:'31x rt'} },
];

function shortAddr(a){return a.slice(0,4)+'…'+a.slice(-4);}
function genAddr(){const c='ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz123456789';let s='';for(let i=0;i<44;i++)s+=c[Math.floor(Math.random()*c.length)];return s;}
function uid(n=6){const c='abcdef0123456789';let s='';for(let i=0;i<n;i++)s+=c[Math.floor(Math.random()*c.length)];return s;}
function ago(ts){const d=Date.now()-ts;const m=Math.floor(d/60000);if(m<1)return'just now';if(m<60)return m+'m ago';const h=Math.floor(m/60);if(h<24)return h+'h ago';return Math.floor(h/24)+'d ago';}
function dur(s){const h=Math.floor(s/3600),m=Math.floor((s%3600)/60),ss=Math.floor(s%60);return(h?h+'h ':'')+(m||h?m+'m ':'')+ss+'s';}
function load(){try{return JSON.parse(localStorage.getItem(LS));}catch(e){return null;}}
function save(s){try{localStorage.setItem(LS,JSON.stringify(s));}catch(e){}}

function useStore(){
  const DEFAULTS={
    wallet:null, demo:false, balance:0, deployments:SEED_DEPLOYMENTS, devices:SEED_DEVICES,
    autoReload:{on:true, threshold:200, amount:1000},
    apiKeys:[
      {id:'k_'+uid(4), name:'production', key:'plos_sk_live_'+uid(8)+uid(8), created:Date.now()-86400e3*12, last:Date.now()-3600e3*5},
      {id:'k_'+uid(4), name:'ci-pipeline', key:'plos_sk_live_'+uid(8)+uid(8), created:Date.now()-86400e3*4, last:Date.now()-86400e3},
    ],
    invoices:[
      {id:'INV-0042', date:Date.now()-86400e3*2, amt:48.20, desc:'Compute · 6 deployments'},
      {id:'INV-0041', date:Date.now()-86400e3*9, amt:122.40, desc:'Compute · 14 deployments'},
      {id:'INV-0040', date:Date.now()-86400e3*16, amt:87.10, desc:'Compute · 9 deployments'},
    ],
    activity:[
      {ic:'check',k:'ok',t:'Deployment <b>sdxl-product-shots</b> completed',ts:Date.now()-7000e3},
      {ic:'coins',k:'',t:'<b>aurora-01</b> earned 4.20 PLOS',ts:Date.now()-5400e3},
      {ic:'check',k:'ok',t:'Deployment <b>whisper-podcast-batch</b> completed',ts:Date.now()-3400e3},
    ]
  };
  const [state,setState]=useState(()=>{ const l=load(); return l?{...DEFAULTS,...l}:DEFAULTS; });
  const update=useCallback((p)=>setState(prev=>{const n=typeof p==='function'?p(prev):{...prev,...p};save(n);return n;}),[]);
  return [state,update];
}

const ToastCtx=createContext(null);
function ToastProvider({children}){
  const [ts,setTs]=useState([]);
  const push=useCallback(t=>{const id=uid();setTs(x=>[...x,{...t,id}]);setTimeout(()=>setTs(x=>x.filter(y=>y.id!==id)),3800);},[]);
  return(<ToastCtx.Provider value={push}>{children}
    <div className="toasts">{ts.map(t=>(<div key={t.id} className={'toast '+(t.kind||'info')}><span className="ti"><Icon n={t.kind==='ok'?'check':'zap'}/></span><div>{t.msg}{t.sub&&<small>{t.sub}</small>}</div></div>))}</div>
  </ToastCtx.Provider>);
}
const useToast=()=>useContext(ToastCtx);

const AppCtx=createContext(null);
function AppProvider({children}){const [state,update]=useStore();return <AppCtx.Provider value={{state,update}}>{children}</AppCtx.Provider>;}
const useApp=()=>useContext(AppCtx);

/* real Phantom wallet (falls back to demo address if not installed) */
async function connectPhantom(){
  const p = (window.phantom && window.phantom.solana) || window.solana;
  if(p && p.isPhantom){
    try{ const resp = await p.connect(); return {address: resp.publicKey.toString(), demo:false}; }
    catch(e){ return null; } /* user rejected */
  }
  return {address: genAddr(), demo:true};
}

Object.assign(window,{Icon,ICONS,TEMPLATES,CATEGORIES,GPUS,REGIONS,SEED_DEVICES,seedLogs,shortAddr,genAddr,uid,ago,dur,connectPhantom,useApp,AppProvider,ToastProvider,useToast});
