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

const C=26,R=18;
type P={x:number,y:number};
type Rival={body:P[],dir:P,color:number};
const D:Record<string,P>={ArrowUp:{x:0,y:-1},w:{x:0,y:-1},ArrowDown:{x:0,y:1},s:{x:0,y:1},ArrowLeft:{x:-1,y:0},a:{x:-1,y:0},ArrowRight:{x:1,y:0},d:{x:1,y:0}};
const same=(a:P,b:P)=>a.x===b.x&&a.y===b.y;

export default function mount(){
 const root=document.querySelector("#game-root") as HTMLElement|null;if(!root)return()=>{};
 render(<main style="min-height:100vh;background:radial-gradient(circle at 50% 10%,#172756,#050713 60%,#010208);color:#eaffff;font-family:ui-monospace,monospace;display:grid;place-items:center;padding:12px;box-sizing:border-box;touch-action:none;user-select:none"><section style="width:min(96vw,980px);display:grid;gap:9px"><header style="display:flex;justify-content:space-between;align-items:end;gap:10px;flex-wrap:wrap"><div><div style="font-size:clamp(25px,5vw,43px);font-weight:950;letter-spacing:.08em;color:#68fff0;text-shadow:0 0 20px #00d9ff">NEON SERPENT 3D</div><div id="objective" style="font-size:12px;color:#a8bdd8">ARENA 1</div></div><div style="display:flex;gap:13px;flex-wrap:wrap;background:#071429dd;border:1px solid #27e7df66;border-radius:9px;padding:8px 12px"><span>ARENA <b id="round">1</b></span><span>SCORE <b id="score" style="color:#ffe65b">0</b></span><span>HIGH <b id="high" style="color:#ff78de">0</b></span><span>ENERGY <b id="energy" style="color:#6affef">0</b></span><span>HEALTH <b id="health" style="color:#ff718e">♥♥♥</b></span></div></header><div id="frame" style="position:relative;border:2px solid #23dfe9;border-radius:12px;overflow:hidden;box-shadow:0 0 28px #00cbe055;background:#030917"><div id="arena" aria-label="Three dimensional neon snake arena" style="width:100%;aspect-ratio:13/9"></div><div id="pause" style="position:absolute;inset:0;display:none;place-items:center;background:#020510aa;color:#75fff0;font-size:clamp(30px,7vw,64px);font-weight:950">PAUSED</div><div id="message" style="position:absolute;inset:0;display:none;place-items:center;text-align:center;background:#020510dc"><div><div id="title" style="font-size:clamp(30px,7vw,62px);font-weight:950"></div><div id="copy" style="margin:9px;color:#bed4e8"></div><button id="retry" style="cursor:pointer;border:1px solid #62fff0;background:#07302e;color:white;border-radius:8px;padding:10px 18px;font:700 14px inherit">RETRY</button></div></div></div><div style="display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap"><span style="font-size:12px;color:#9fb2cd">ARROWS / WASD · SWIPE / PAD · P PAUSE · GOLD ENERGY · AVOID PURPLE POISON</span><button id="restart" style="cursor:pointer;border:1px solid #ff4c88;background:#290922;color:white;border-radius:8px;padding:10px 18px;font:700 14px inherit">RESTART RUN</button></div><div style="display:grid;grid-template-columns:repeat(3,50px);grid-template-rows:repeat(2,40px);gap:5px;justify-content:center"><button data-d="u" style="grid-column:2">▲</button><button data-d="l">◀</button><button data-d="d">▼</button><button data-d="r">▶</button></div></section></main>,root);
 const q=(s:string)=>root.querySelector(s) as HTMLElement,host=q("#arena"),scoreEl=q("#score"),highEl=q("#high"),roundEl=q("#round"),energyEl=q("#energy"),healthEl=q("#health"),objective=q("#objective"),msg=q("#message"),pauseEl=q("#pause"),title=q("#title"),copy=q("#copy"),restart=q("#restart"),retryBtn=q("#retry");
 const pads=Array.from(root.querySelectorAll("button[data-d]")) as HTMLButtonElement[];pads.forEach(b=>b.style.cssText+=";border:1px solid #24dce777;background:#07182d;color:#70fff1;border-radius:7px;font:700 18px inherit;cursor:pointer");
 const renderer=new THREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio||1,2));renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.shadowMap.enabled=true;renderer.domElement.style.cssText="display:block;width:100%;height:100%";host.appendChild(renderer.domElement);
 const scene=new THREE.Scene();scene.background=new THREE.Color(0x020510);scene.fog=new THREE.Fog(0x020510,20,42);
 const camera=new THREE.PerspectiveCamera(45,13/9,.1,100);camera.position.set(0,19,19);camera.lookAt(0,0,0);
 scene.add(new THREE.HemisphereLight(0x80dfff,0x080315,1.35));const sun=new THREE.DirectionalLight(0xffffff,1.5);sun.position.set(-6,15,8);sun.castShadow=true;scene.add(sun);
 const floor=new THREE.Mesh(new THREE.PlaneGeometry(C,R),new THREE.MeshStandardMaterial({color:0x06152b,roughness:.78,metalness:.25}));floor.rotation.x=-Math.PI/2;floor.receiveShadow=true;scene.add(floor);
 const grid=new THREE.GridHelper(C,C,0x24dfea,0x12375a);grid.scale.z=R/C;grid.position.y=.015;scene.add(grid);
 const borderMat=new THREE.MeshStandardMaterial({color:0x09264a,emissive:0x00bace,emissiveIntensity:.7});
 [[0,-R/2-.25,C+.8,.5],[0,R/2+.25,C+.8,.5],[-C/2-.25,0,.5,R],[C/2+.25,0,.5,R]].forEach(v=>{const m=new THREE.Mesh(new THREE.BoxGeometry(v[2],.55,v[3]),borderMat);m.position.set(v[0],.26,v[1]);scene.add(m)});
 const dynamic=new THREE.Group();scene.add(dynamic);const geos=[new THREE.BoxGeometry(.82,.62,.82),new THREE.SphereGeometry(.34,16,12),new THREE.CylinderGeometry(.38,.38,.85,8)];const mats:THREE.Material[]=[];
 let snake:P[]=[],rivals:Rival[]=[],walls:P[]=[],food:P={x:0,y:0},poison:P={x:0,y:0},dir={x:1,y:0},next={x:1,y:0};
 let score=0,high=0,round=1,eaten=0,health=3,state:"play"|"pause"|"lost"|"won"="play",acc=0,last=performance.now(),raf=0,wallPhase=0,invincible=0,swipe:P|null=null;
 const goals=[5,7,9,11,14],names=["FEED THE SURGE","DODGE THE DRIFTERS","OUTRUN THE HUNTER","RIVAL GAUNTLET","COLOSSUS CROWN"];
 const world=(p:P)=>({x:p.x-C/2+.5,z:p.y-R/2+.5});
 function blocked(p:P){return snake.some(x=>same(x,p))||walls.some(x=>same(x,p))||rivals.some(r=>r.body.some(x=>same(x,p)))}
 function free(){for(let i=0;i<500;i++){const p={x:1+Math.floor(Math.random()*(C-2)),y:1+Math.floor(Math.random()*(R-2))};if(!blocked(p)&&!same(p,food)&&!same(p,poison))return p}return{x:2,y:2}}
 function makeRival(i:number):Rival{const h=free(),d=i%2?{x:0,y:1}:{x:-1,y:0};return{body:[h,{x:h.x-d.x,y:h.y-d.y},{x:h.x-d.x*2,y:h.y-d.y*2}],dir:d,color:i%2?0xff4e9d:0xff823f}}
 function arena(){walls=[];for(let k=0;k<Math.min(3,round-1);k++){const x=8+k*6;for(let y=3;y<R-3;y++)if(y!==5+k*3&&y!==6+k*3)walls.push({x,y})}rivals=[];for(let i=0;i<Math.max(0,round-2);i++)rivals.push(makeRival(i));food=free();poison=free()}
 function hud(){scoreEl.textContent=String(score);high=Math.max(high,score);highEl.textContent=String(high);roundEl.textContent=String(round);energyEl.textContent=`${eaten}/${goals[round-1]}`;healthEl.textContent="♥".repeat(health)+"♡".repeat(3-health);objective.textContent=`ARENA ${round} · ${names[round-1]} · COLLECT ${goals[round-1]} ENERGY`}
 function reset(){snake=[{x:5,y:9},{x:4,y:9},{x:3,y:9},{x:2,y:9}];dir=next={x:1,y:0};score=0;round=1;eaten=0;health=3;state="play";acc=0;last=performance.now();invincible=0;msg.style.display="none";pauseEl.style.display="none";arena();hud()}
 function end(win=false){state=win?"won":"lost";title.textContent=win?"COLOSSUS CONQUERED":"SERPENT FALLEN";title.style.color=win?"#68ffe9":"#ff527c";copy.textContent=`Score ${score} · High ${high} · Press Enter or Retry`;msg.style.display="grid"}
 function steer(n:P){if(state==="play"&&(n.x!==-dir.x||n.y!==-dir.y))next=n}
 function hurt(now:number){if(now<invincible)return;health--;invincible=now+1200;if(health<=0)end();else{snake=snake.slice(0,Math.max(3,snake.length-2));score=Math.max(0,score-200);hud()}}
 function advance(){score+=500*round;if(round===5){end(true);return}round++;eaten=0;snake=[{x:4,y:9},{x:3,y:9},{x:2,y:9}];dir=next={x:1,y:0};arena();hud()}
 function moveRivals(now:number){rivals.forEach(r=>{const h=r.body[0],target=snake[0];const opts=[r.dir,{x:r.dir.y,y:-r.dir.x},{x:-r.dir.y,y:r.dir.x}];if(Math.random()<.65)opts.sort((a,b)=>Math.abs(h.x+a.x-target.x)+Math.abs(h.y+a.y-target.y)-Math.abs(h.x+b.x-target.x)-Math.abs(h.y+b.y-target.y));let nh:P|null=null;for(const d of opts){const p={x:h.x+d.x,y:h.y+d.y};if(p.x>0&&p.x<C-1&&p.y>0&&p.y<R-1&&!walls.some(w=>same(w,p))&&!r.body.some(x=>same(x,p))){r.dir=d;nh=p;break}}if(!nh)return;r.body.unshift(nh);if(same(nh,food)){score=Math.max(0,score-100);food=free();r.body.push({...r.body[r.body.length-1]})}r.body.pop();if(r.body.some(x=>same(x,snake[0])))hurt(now)})}
 function step(now:number){dir=next;const h={x:snake[0].x+dir.x,y:snake[0].y+dir.y};if(h.x<0||h.x>=C||h.y<0||h.y>=R||snake.some(x=>same(x,h))||walls.some(x=>same(x,h))){hurt(now);if(state==="play"){dir=next={x:-dir.x,y:-dir.y};snake[0]={x:Math.max(1,Math.min(C-2,snake[0].x)),y:Math.max(1,Math.min(R-2,snake[0].y))}}return}snake.unshift(h);let grow=false;if(same(h,food)){eaten++;score+=100*round+snake.length*4;grow=true;food=free();if(eaten>=goals[round-1]){advance();return}}if(same(h,poison)){score=Math.max(0,score-150);poison=free();hurt(now)}if(rivals.some(r=>r.body.some(x=>same(x,h))))hurt(now);if(!grow)snake.pop();moveRivals(now);hud()}
 function moveWalls(){if(round<2)return;wallPhase++;const shift=wallPhase%2?1:-1;walls=walls.map((p,i)=>i%5===0?{x:p.x,y:Math.max(2,Math.min(R-3,p.y+shift))}:p)}
 function mat(color:number,glow=.65){const m=new THREE.MeshStandardMaterial({color,emissive:color,emissiveIntensity:glow,roughness:.28,metalness:.35});mats.push(m);return m}
 function cube(p:P,color:number,y=.4,scale=1){const m=new THREE.Mesh(geos[0],mat(color));const w=world(p);m.position.set(w.x,y,w.z);m.scale.setScalar(scale);m.castShadow=true;dynamic.add(m);return m}
 function draw(now:number){while(dynamic.children.length)dynamic.remove(dynamic.children[0]);mats.splice(0).forEach(m=>m.dispose());walls.forEach(p=>cube(p,0xff315f,.45,1));rivals.forEach(r=>r.body.forEach((p,i)=>cube(p,r.color,.4,i? .88:1.05)));snake.forEach((p,i)=>{const m=cube(p,i?0x18e8c4:0xefffff,.43,i? .9:1.08);if(now<invincible&&Math.floor(now/90)%2)m.visible=false});const fw=world(food),f=new THREE.Mesh(geos[1],mat(0xffe750,1.6));f.position.set(fw.x,.55+.12*Math.sin(now*.008),fw.z);f.scale.setScalar(1+.12*Math.sin(now*.01));dynamic.add(f);const pw=world(poison),p=new THREE.Mesh(geos[2],mat(0xa64dff,1.4));p.position.set(pw.x,.52,pw.z);p.rotation.z=Math.PI/2;p.rotation.y=now*.002;dynamic.add(p);renderer.render(scene,camera)}
 function resize(){const r=host.getBoundingClientRect();renderer.setSize(r.width,r.height,false);camera.aspect=r.width/r.height;camera.updateProjectionMatrix()}
 function loop(now:number){const dt=Math.min(80,now-last);last=now;if(state==="play"){acc+=dt;const speed=Math.max(62,145-round*14-eaten*2);while(acc>=speed&&state==="play"){step(now);acc-=speed}if(round>1&&Math.floor(now/1100)!==Math.floor((now-dt)/1100))moveWalls()}draw(now);raf=requestAnimationFrame(loop)}
 const key=(e:KeyboardEvent)=>{const d=D[e.key]||D[e.key.toLowerCase()];if(d){e.preventDefault();steer(d)}if(e.key.toLowerCase()==="p"&&(state==="play"||state==="pause")){state=state==="play"?"pause":"play";pauseEl.style.display=state==="pause"?"grid":"none"}if(e.key==="Enter"&&(state==="lost"||state==="won"))reset()};
 const down=(e:PointerEvent)=>swipe={x:e.clientX,y:e.clientY},up=(e:PointerEvent)=>{if(!swipe)return;const x=e.clientX-swipe.x,y=e.clientY-swipe.y;if(Math.max(Math.abs(x),Math.abs(y))>14)steer(Math.abs(x)>Math.abs(y)?{x:Math.sign(x),y:0}:{x:0,y:Math.sign(y)});swipe=null};
 const map:Record<string,P>={u:{x:0,y:-1},d:{x:0,y:1},l:{x:-1,y:0},r:{x:1,y:0}},ph=pads.map(b=>{const f=()=>steer(map[b.dataset.d!]);b.addEventListener("pointerdown",f);return f});
 window.addEventListener("keydown",key);window.addEventListener("resize",resize);renderer.domElement.addEventListener("pointerdown",down);renderer.domElement.addEventListener("pointerup",up);restart.addEventListener("click",reset);retryBtn.addEventListener("click",reset);resize();reset();raf=requestAnimationFrame(loop);
 return()=>{cancelAnimationFrame(raf);window.removeEventListener("keydown",key);window.removeEventListener("resize",resize);renderer.domElement.removeEventListener("pointerdown",down);renderer.domElement.removeEventListener("pointerup",up);restart.removeEventListener("click",reset);retryBtn.removeEventListener("click",reset);pads.forEach((b,i)=>b.removeEventListener("pointerdown",ph[i]));geos.forEach(g=>g.dispose());mats.forEach(m=>m.dispose());floor.geometry.dispose();(floor.material as THREE.Material).dispose();borderMat.dispose();renderer.dispose();renderer.domElement.remove();render(null,root)}
}