import { render } from "tradjs/client";
import * as THREE from "three";

type Orb={m:THREE.Mesh,x:number,z:number,vx:number,vz:number,life:number,hostile:boolean,dmg:number};
type Foe={m:THREE.Mesh,x:number,z:number,hp:number,r:number,cool:number,elite:boolean};
type Mod="fury"|"glass"|"storm";

export default function mount(){
  const root=document.querySelector("#game-root") as HTMLElement;
  const mounted=render(<div style={{minHeight:"100vh",background:"radial-gradient(circle at top,#192c4b,#02040a 72%)",color:"#effcff",fontFamily:"system-ui,sans-serif",display:"grid",placeItems:"center"}}><main style={{width:"min(1080px,96vw)"}}>
    <header style={{display:"flex",justifyContent:"space-between",alignItems:"center",gap:"12px",marginBottom:"8px"}}><div><div style={{fontSize:"23px",fontWeight:"950",letterSpacing:"3px"}}>NEON SWARM // BOSS GAUNTLET</div><div style={{fontSize:"12px",color:"#aebed2"}}>WASD / arrows move · Aim + hold pointer fire · 1–3 toggle unlocked trials · R retry</div></div><button id="restart" style={{border:0,borderRadius:"8px",padding:"10px 18px",background:"#58f0c0",fontWeight:"900",cursor:"pointer"}}>Restart Run</button></header>
    <section id="stage" style={{height:"min(66vw,650px)",minHeight:"430px",position:"relative",overflow:"hidden",border:"1px solid #416184",borderRadius:"13px",background:"#050914",touchAction:"none",cursor:"crosshair"}}>
      <div style={{position:"absolute",zIndex:3,inset:"13px 15px auto",display:"flex",justifyContent:"space-between",pointerEvents:"none",fontWeight:"850",textShadow:"0 2px 5px #000"}}><div><div style={{width:"230px",height:"14px",background:"#17233a",border:"1px solid #68809e"}}><div id="hp" style={{height:"100%",background:"#55efbe"}}/></div><div id="hptext" style={{fontSize:"11px",marginTop:"3px"}}/></div><div id="stats" style={{textAlign:"right",lineHeight:"1.45"}}/></div>
      <div id="bossui" style={{display:"none",position:"absolute",zIndex:3,left:"25%",right:"25%",top:"15px",textAlign:"center",pointerEvents:"none"}}><div id="bossname" style={{fontWeight:"950",letterSpacing:"2px",color:"#ff7897"}}/><div style={{height:"9px",background:"#281529",border:"1px solid #a14a72"}}><div id="bosshp" style={{height:"100%",background:"#ff4e7d"}}/></div></div>
      <div id="banner" style={{position:"absolute",zIndex:3,inset:"39% 0 auto",textAlign:"center",fontSize:"31px",fontWeight:"950",color:"#61f4c6",textShadow:"0 0 20px #14d9ff",pointerEvents:"none"}}/>
      <div style={{position:"absolute",zIndex:3,left:"10px",bottom:"9px",padding:"7px",borderRadius:"6px",background:"#020611c9",fontSize:"11px",color:"#c8d7e9",pointerEvents:"none"}}>CHECKPOINT BOSSES: waves 3, 6, 9<br/><span id="mods"/></div>
      <div id="end" style={{display:"none",position:"absolute",zIndex:5,inset:0,background:"#02050bea",placeItems:"center",textAlign:"center"}}><div><div id="title" style={{fontSize:"42px",fontWeight:"950"}}/><div id="summary" style={{margin:"8px 0 18px",color:"#b9c9dc",lineHeight:"1.6"}}/><button id="retry" style={{border:0,borderRadius:"8px",padding:"11px 24px",background:"#58f0c0",fontWeight:"900",cursor:"pointer"}}>Replay Seed</button></div></div>
    </section></main></div>,root);
  const q=<T extends Element>(s:string)=>root.querySelector(s) as T,stage=q<HTMLElement>("#stage"),hp=q<HTMLElement>("#hp"),hptext=q<HTMLElement>("#hptext"),stats=q<HTMLElement>("#stats"),banner=q<HTMLElement>("#banner"),bossui=q<HTMLElement>("#bossui"),bossname=q<HTMLElement>("#bossname"),bosshp=q<HTMLElement>("#bosshp"),modsEl=q<HTMLElement>("#mods"),end=q<HTMLElement>("#end"),title=q<HTMLElement>("#title"),summary=q<HTMLElement>("#summary"),restart=q<HTMLButtonElement>("#restart"),retry=q<HTMLButtonElement>("#retry");
  const scene=new THREE.Scene();scene.background=new THREE.Color(0x050914);scene.fog=new THREE.Fog(0x050914,27,55);
  const camera=new THREE.PerspectiveCamera(48,1,.1,100);camera.position.set(0,27,25);camera.lookAt(0,0,0);
  const renderer=new THREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio,2));renderer.shadowMap.enabled=true;Object.assign(renderer.domElement.style,{width:"100%",height:"100%",display:"block"});stage.prepend(renderer.domElement);
  scene.add(new THREE.HemisphereLight(0x9cecff,0x07101c,1.8));const sun=new THREE.DirectionalLight(0xffffff,1.5);sun.position.set(7,16,8);sun.castShadow=true;scene.add(sun);
  const floor=new THREE.Mesh(new THREE.PlaneGeometry(34,22),new THREE.MeshStandardMaterial({color:0x091426,metalness:.55,roughness:.72}));floor.rotation.x=-Math.PI/2;floor.receiveShadow=true;scene.add(floor);const grid=new THREE.GridHelper(34,17,0x286480,0x132941);grid.position.y=.02;scene.add(grid);
  const playerM=new THREE.Mesh(new THREE.CylinderGeometry(.48,.7,.78,8),new THREE.MeshStandardMaterial({color:0x55efbe,emissive:0x13755d,emissiveIntensity:1.3}));scene.add(playerM);const gun=new THREE.Mesh(new THREE.BoxGeometry(.23,.22,1.1),new THREE.MeshStandardMaterial({color:0xe9ffff,emissive:0x48dfff}));gun.position.set(0,.2,-.65);playerM.add(gun);
  const keys=new Set<string>(),mouse={x:0,z:-5,down:false},ray=new THREE.Raycaster(),plane=new THREE.Plane(new THREE.Vector3(0,1,0),0),hit=new THREE.Vector3();
  let foes:Foe[]=[],orbs:Orb[]=[],player={x:0,z:5,hp:100,inv:0},boss:Foe|null=null,state:"play"|"lost"|"won"="play";
  let seed=73421,rngState=seed,wave=1,spawned=0,quota=8,kills=0,score=0,combo=1,comboT=0,spawnT=.4,fireT=0,bannerT=2,last=performance.now(),raf=0,bossPhase=0,phaseT=0,runTime=0;
  const unlocked=new Set<Mod>(),active=new Set<Mod>();
  const rand=()=>{rngState=(rngState*1664525+1013904223)>>>0;return rngState/4294967296;};
  function mat(color:number){return new THREE.MeshStandardMaterial({color,emissive:color,emissiveIntensity:.55});}
  function dispose(m:THREE.Mesh){scene.remove(m);m.geometry.dispose();(Array.isArray(m.material)?m.material:[m.material]).forEach(x=>x.dispose());}
  function clear(){foes.forEach(f=>dispose(f.m));orbs.forEach(o=>dispose(o.m));foes=[];orbs=[];boss=null;}
  function orb(x:number,z:number,vx:number,vz:number,hostile:boolean,dmg:number){const m=new THREE.Mesh(new THREE.SphereGeometry(hostile?.16:.11,8,6),new THREE.MeshBasicMaterial({color:hostile?0xff5d77:0x6ff5ff}));scene.add(m);orbs.push({m,x,z,vx,vz,life:hostile?6:1.6,hostile,dmg});}
  function enemy(){const a=rand()*Math.PI*2,x=Math.cos(a)*16,z=Math.sin(a)*9.5,elite=wave>4&&rand()<.18,hp0=(3+wave*.65)*(elite?2.2:1);const m=new THREE.Mesh(elite?new THREE.OctahedronGeometry(.78):new THREE.ConeGeometry(.58,.95,5),mat(elite?0xb268ff:0xff4f70));scene.add(m);foes.push({m,x,z,hp:hp0,r:elite?.78:.58,cool:.5+rand(),elite});spawned++;}
  function summonBoss(){const tier=wave/3,m=new THREE.Mesh(tier===1?new THREE.DodecahedronGeometry(1.65):tier===2?new THREE.IcosahedronGeometry(1.8):new THREE.TorusKnotGeometry(1.25,.42,64,8),mat(tier===1?0xff3f72:tier===2?0xff9d3c:0xc55cff));scene.add(m);boss={m,x:0,z:-5,hp:90+tier*75,r:1.8,cool:1,elite:true};foes.push(boss);bossPhase=1;phaseT=1.5;bannerT=2.5;}
  function radial(f:Foe,n:number,speed:number){for(let i=0;i<n;i++){const a=i*Math.PI*2/n+runTime*.35;orb(f.x,f.z,Math.cos(a)*speed,Math.sin(a)*speed,true,9+(active.has("fury")?4:0));}}
  function hurt(n:number){if(player.inv>0)return;player.hp-=n;player.inv=.55;if(player.hp<=0)finish(false);}
  function kill(f:Foe){const wasBoss=f===boss;score+=Math.round((wasBoss?1500:100)*combo*(active.size+1));kills++;combo=Math.min(8,combo+.25);comboT=2.5;f.hp=0;if(wasBoss){boss=null;const u:Mod=wave===3?"fury":wave===6?"glass":"storm";unlocked.add(u);active.delete(u);bannerT=3;} }
  function rank(){return score>=24000?"S":score>=15000?"A":score>=9000?"B":score>=4500?"C":"D";}
  function finish(win:boolean){state=win?"won":"lost";mouse.down=false;title.textContent=win?"GAUNTLET MASTERED":"RUN TERMINATED";title.style.color=win?"#58f0c0":"#ff5577";summary.innerHTML=`RANK <b style="font-size:25px;color:#ffe36a">${rank()}</b> · SCORE ${score}<br/>Seed ${seed} · Wave ${wave} · ${kills} kills · ${runTime.toFixed(1)}s<br/>Unlocked trials persist for this session.`;end.style.display="grid";}
  function reset(){clear();rngState=seed;player={x:0,z:5,hp:active.has("glass")?55:100,inv:0};wave=1;spawned=kills=score=0;quota=8;combo=1;comboT=0;spawnT=.4;fireT=0;bannerT=2;bossPhase=0;phaseT=0;runTime=0;state="play";end.style.display="none";mouse.down=false;last=performance.now();}
  function advance(){if(wave===9){finish(true);return;}wave++;spawned=0;quota=7+wave*2;player.hp=Math.min(active.has("glass")?55:100,player.hp+16);spawnT=1;bannerT=2;}
  function update(dt:number){if(state!=="play")return;runTime+=dt;player.inv-=dt;fireT-=dt;spawnT-=dt;bannerT-=dt;comboT-=dt;if(comboT<=0)combo=1;
    let dx=Number(keys.has("d")||keys.has("arrowright"))-Number(keys.has("a")||keys.has("arrowleft")),dz=Number(keys.has("s")||keys.has("arrowdown"))-Number(keys.has("w")||keys.has("arrowup")),d=Math.hypot(dx,dz)||1;player.x=THREE.MathUtils.clamp(player.x+dx/d*9.5*dt,-15.3,15.3);player.z=THREE.MathUtils.clamp(player.z+dz/d*9.5*dt,-9.3,9.3);
    const aim=Math.atan2(mouse.x-player.x,mouse.z-player.z);playerM.rotation.y=aim;if(mouse.down&&fireT<=0){orb(player.x,player.z,Math.sin(aim)*27,Math.cos(aim)*27,false,active.has("glass")?3:1.8);fireT=.14;}
    if(wave%3===0){if(!boss&&spawned===0){summonBoss();spawned=quota;} }else if(spawned<quota&&spawnT<=0){enemy();spawnT=Math.max(.2,.72-wave*.035);}
    for(const f of foes){if(f.hp<=0)continue;f.cool-=dt;let dx=player.x-f.x,dz=player.z-f.z,dist=Math.hypot(dx,dz)||1,nx=dx/dist,nz=dz/dist;if(f===boss){const max=90+(wave/3)*75,p=f.hp/max,newPhase=p>.66?1:p>.33?2:3;if(newPhase!==bossPhase){bossPhase=newPhase;phaseT=1;bannerT=1.5;}phaseT-=dt;f.x=Math.sin(runTime*(.55+bossPhase*.12))*8;f.z=-4+Math.cos(runTime*.7)*2;if(f.cool<=0){radial(f,5+bossPhase*3,4.5+bossPhase);if(bossPhase>=2){const a=Math.atan2(dz,dx);for(let i=-1;i<=1;i++)orb(f.x,f.z,Math.cos(a+i*.18)*8,Math.sin(a+i*.18)*8,true,12);}if(bossPhase===3)for(let i=0;i<2;i++)enemy();f.cool=Math.max(.48,1.45-bossPhase*.22-(active.has("fury")?.2:0));}f.m.rotation.x+=dt;f.m.rotation.y+=dt*1.7;}else{const speed=(2.1+wave*.1)*(f.elite?1.2:1)*(active.has("fury")?1.25:1);f.x+=nx*speed*dt;f.z+=nz*speed*dt;if(f.elite&&f.cool<=0){orb(f.x,f.z,nx*7,nz*7,true,10);f.cool=1.5;}if(dist<f.r+.65)hurt(f.elite?16:11);}f.m.position.set(f.x,f===boss?1.6:.55,f.z);f.m.lookAt(player.x,.4,player.z);}
    if(active.has("storm")&&Math.floor(runTime*2)!==Math.floor((runTime-dt)*2)){const a=rand()*Math.PI*2;orb(Math.cos(a)*15,Math.sin(a)*9,-Math.cos(a)*8,-Math.sin(a)*8,true,9);}
    for(const o of orbs){o.x+=o.vx*dt;o.z+=o.vz*dt;o.life-=dt;o.m.position.set(o.x,.45,o.z);if(o.hostile&&Math.hypot(o.x-player.x,o.z-player.z)<.65){hurt(o.dmg);o.life=0;}if(!o.hostile)for(const f of foes)if(f.hp>0&&Math.hypot(o.x-f.x,o.z-f.z)<f.r+.18){f.hp-=o.dmg;o.life=0;if(f.hp<=0)kill(f);break;}}
    const dead=foes.filter(f=>f.hp<=0),gone=orbs.filter(o=>o.life<=0||Math.abs(o.x)>18||Math.abs(o.z)>12);dead.forEach(f=>dispose(f.m));gone.forEach(o=>dispose(o.m));foes=foes.filter(f=>f.hp>0);orbs=orbs.filter(o=>!gone.includes(o));
    if(spawned>=quota&&foes.length===0)advance();
  }
  function draw(){playerM.position.set(player.x,.4,player.z);playerM.visible=player.inv<=0||Math.floor(player.inv*18)%2===0;const max=active.has("glass")?55:100;hp.style.width=`${Math.max(0,player.hp/max*100)}%`;hp.style.background=player.hp/max>.35?"#55efbe":"#ff5577";hptext.textContent=`CORE ${Math.ceil(player.hp)} / ${max}`;stats.innerHTML=`SCORE ${score} · RANK ${rank()}<br/>WAVE ${wave}/9 · COMBO x${combo.toFixed(2)}<br/><span style="font-size:11px;color:#b6c7da">SEED ${seed}</span>`;modsEl.innerHTML=(["fury","glass","storm"] as Mod[]).map((m,i)=>`<span style="color:${active.has(m)?"#ffdc62":unlocked.has(m)?"#73f1c7":"#68778a"}">${i+1} ${m.toUpperCase()}${unlocked.has(m)?active.has(m)?" ON":" READY":" LOCKED"}</span>`).join(" · ");banner.textContent=bannerT>0?(boss?`OVERSEER // PHASE ${bossPhase}`:wave%3===0&&spawned?`TRIAL UNLOCKED`: `WAVE ${wave} // ${active.size?"MODIFIED":"STANDARD"}`):"";bossui.style.display=boss?"block":"none";if(boss){bossname.textContent=`${wave===3?"THE WARDEN":wave===6?"THE ARCHITECT":"THE SINGULARITY"} · PHASE ${bossPhase}`;bosshp.style.width=`${Math.max(0,boss.hp/(90+(wave/3)*75)*100)}%`;}renderer.render(scene,camera);}
  function frame(now:number){const dt=Math.min(.033,(now-last)/1000);last=now;update(dt);draw();raf=requestAnimationFrame(frame);}
  function resize(){const r=stage.getBoundingClientRect();renderer.setSize(r.width,r.height,false);camera.aspect=r.width/r.height;camera.updateProjectionMatrix();}
  function pointer(e:PointerEvent){const r=renderer.domElement.getBoundingClientRect(),v=new THREE.Vector2((e.clientX-r.left)/r.width*2-1,-((e.clientY-r.top)/r.height)*2+1);ray.setFromCamera(v,camera);if(ray.ray.intersectPlane(plane,hit)){mouse.x=hit.x;mouse.z=hit.z;}}
  const kd=(e:KeyboardEvent)=>{const k=e.key.toLowerCase();keys.add(k);if(["w","a","s","d","arrowup","arrowdown","arrowleft","arrowright"].includes(k))e.preventDefault();if(k==="r"&&state!=="play")reset();const map:Record<string,Mod>={"1":"fury","2":"glass","3":"storm"};if(map[k]&&unlocked.has(map[k])&&state!=="play"){active.has(map[k])?active.delete(map[k]):active.add(map[k]);draw();}},ku=(e:KeyboardEvent)=>keys.delete(e.key.toLowerCase()),down=(e:PointerEvent)=>{pointer(e);mouse.down=true;renderer.domElement.setPointerCapture(e.pointerId);},up=()=>mouse.down=false;
  window.addEventListener("resize",resize);window.addEventListener("keydown",kd);window.addEventListener("keyup",ku);renderer.domElement.addEventListener("pointermove",pointer);renderer.domElement.addEventListener("pointerdown",down);window.addEventListener("pointerup",up);restart.addEventListener("click",reset);retry.addEventListener("click",reset);resize();reset();raf=requestAnimationFrame(frame);
  return()=>{cancelAnimationFrame(raf);window.removeEventListener("resize",resize);window.removeEventListener("keydown",kd);window.removeEventListener("keyup",ku);renderer.domElement.removeEventListener("pointermove",pointer);renderer.domElement.removeEventListener("pointerdown",down);window.removeEventListener("pointerup",up);restart.removeEventListener("click",reset);retry.removeEventListener("click",reset);clear();scene.traverse(o=>{const m=o as THREE.Mesh;if(m.geometry)m.geometry.dispose();if(m.material)(Array.isArray(m.material)?m.material:[m.material]).forEach(x=>x.dispose());});renderer.dispose();renderer.domElement.remove();if(typeof mounted==="function")mounted();};
}