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

const C=10,R=20;
const COLORS=[0,0x14f195,0x9945ff,0x00d1ff,0xffcc33,0xff4d8d,0xff7a45,0x7c83ff];
const SHAPES=[[[0,0],[1,0],[-1,0],[2,0]],[[0,0],[-1,0],[0,-1],[1,-1]],[[0,0],[1,0],[-1,-1],[0,-1]],[[0,0],[-1,0],[1,0],[1,-1]],[[0,0],[-1,0],[1,0],[-1,-1]],[[0,0],[1,0],[0,-1],[1,-1]],[[0,0],[-1,0],[1,0],[0,-1]]];
type Mode="Sprint"|"Marathon"|"Score Attack";
type P={c:number[][];x:number;y:number;k:number};
const best:Record<Mode,number>={Sprint:0,Marathon:0,"Score Attack":0};

export default function mount(){
 const root=document.querySelector("#game-root") as HTMLElement;
 const dispose=render(<div class="app"><style>{`
 .app{min-height:100%;box-sizing:border-box;padding:12px;background:radial-gradient(circle at 30% 0,#281354,#090713 62%);color:#f8f6ff;font-family:system-ui;display:flex;align-items:center;justify-content:center;user-select:none;touch-action:none}.shell{display:grid;grid-template-columns:minmax(260px,430px) 155px;gap:12px;width:min(96vw,600px)}.arena{position:relative;min-height:560px;border:2px solid #9945ff;border-radius:14px;overflow:hidden;background:#070611;box-shadow:0 0 32px #9945ff66}.webgl{position:absolute;inset:0}.webgl canvas{display:block;width:100%;height:100%}.side{display:flex;flex-direction:column;gap:7px}.title{font-size:23px;font-weight:950;line-height:.9;background:linear-gradient(90deg,#14f195,#00d1ff,#9945ff);color:transparent;background-clip:text}.card{background:#171229;border:1px solid #49376f;border-radius:9px;padding:8px}.label{font-size:9px;letter-spacing:.12em;color:#aaa0c6;text-transform:uppercase}.value{font-size:19px;font-weight:900;color:#14f195}.meta{font-size:11px;color:#c9c1dc}button{border:1px solid #7056a5;border-radius:8px;background:#241b3d;color:white;padding:8px 5px;font-weight:800;cursor:pointer}.restart{background:linear-gradient(90deg,#14f195,#00d1ff);color:#06150f;border:0}.modes{display:grid;gap:4px}.controls{grid-column:1/-1;display:grid;grid-template-columns:repeat(6,1fr);gap:5px}.help{grid-column:1/-1;text-align:center;color:#aaa0c6;font-size:11px}.overlay{position:absolute;inset:0;display:none;place-items:center;text-align:center;background:#080612df;padding:25px;z-index:2}.overlay.show{display:grid}.overlay h2{color:#14f195;font-size:28px;margin:0}.overlay button{width:100%;margin-top:10px}.challenge{font-size:11px;color:#ffcc33}@media(max-width:590px){.shell{grid-template-columns:minmax(220px,1fr) 105px;gap:6px}.arena{min-height:460px}.title{font-size:16px}.card{padding:5px}.value{font-size:14px}.help{display:none}}
 `}</style><div class="shell"><div class="arena"><div class="webgl"/><div class="overlay"><div><h2 class="result">RUN OVER</h2><p class="final"/><button class="restart">Retry</button></div></div></div><aside class="side"><div class="title">SOLANA<br/>STACK 3D</div><div class="modes"><button data-mode="Sprint">Sprint · 20</button><button data-mode="Marathon">Marathon · 40</button><button data-mode="Score Attack">Attack · 2:00</button></div><div class="card"><div class="label mode">Sprint</div><div class="value score">0</div><div class="meta lines">0 lines</div></div><div class="card"><div class="label">Time</div><b class="time">0.0</b><div class="label">Best</div><b class="best">—</b></div><div class="card challenge">Clear four lines<br/><b class="challengeState">0 / 4</b></div><div class="card"><div class="label">Hold</div><b class="held">—</b><div class="label">Next</div><b class="next">—</b></div><button data-act="hold">Hold · C</button><button class="restart">Restart</button></aside><div class="controls"><button data-act="left">◀</button><button data-act="rotate">↻</button><button data-act="right">▶</button><button data-act="down">▼</button><button data-act="drop">⤓</button><button data-act="hold">HOLD</button></div><div class="help">← → move · ↑ rotate · ↓ soft drop · Space slam · C hold · R restart · swipe/tap</div></div></div>,root);
 const host=root.querySelector(".webgl") as HTMLElement;
 const scene=new THREE.Scene();scene.background=new THREE.Color(0x070611);scene.fog=new THREE.Fog(0x070611,19,34);
 const camera=new THREE.PerspectiveCamera(34,1,.1,100);camera.position.set(0,1.2,29);camera.lookAt(0,0,0);
 const renderer=new THREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio,2));renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.shadowMap.enabled=true;host.appendChild(renderer.domElement);
 scene.add(new THREE.HemisphereLight(0xaac8ff,0x12051f,2.1));const keyLight=new THREE.DirectionalLight(0xffffff,2.5);keyLight.position.set(-6,9,12);keyLight.castShadow=true;scene.add(keyLight);const neon=new THREE.PointLight(0x9945ff,45,25);neon.position.set(7,3,8);scene.add(neon);
 const frameMat=new THREE.MeshStandardMaterial({color:0x1a1230,metalness:.75,roughness:.3,emissive:0x210e45,emissiveIntensity:.5});
 const boxGeo=new THREE.BoxGeometry(.88,.88,.88),ghostMat=new THREE.MeshBasicMaterial({color:0x14f195,transparent:true,opacity:.16,wireframe:true});
 const frame=(w:number,h:number,x:number,y:number)=>{const m=new THREE.Mesh(new THREE.BoxGeometry(w,h,.65),frameMat);m.position.set(x,y,-.65);scene.add(m)};
 frame(.45,20.7,-5.25,0);frame(.45,20.7,5.25,0);frame(10.9,.45,0,-10.25);frame(10.9,.35,0,10.25);
 const floor=new THREE.Mesh(new THREE.PlaneGeometry(10,20),new THREE.MeshStandardMaterial({color:0x0c1020,roughness:.8,metalness:.2}));floor.position.z=-1;scene.add(floor);
 const grid=new THREE.GridHelper(20,20,0x43226f,0x201438);grid.rotation.x=Math.PI/2;grid.scale.x=.5;grid.position.z=-.92;scene.add(grid);
 const blocks=new THREE.Group();scene.add(blocks);const mats=COLORS.slice(1).map(c=>new THREE.MeshStandardMaterial({color:c,emissive:c,emissiveIntensity:.25,metalness:.28,roughness:.25}));
 const board:number[][]=Array.from({length:R},()=>Array(C).fill(0));let mode:Mode="Sprint",p:P,bag:number[]=[],queue:number[]=[],held:number|null=null,canHold=true,score=0,lines=0,combo=-1,over=false,start=0,elapsed=0,last=0,fall=0,raf=0,tx=0,ty=0;
 const $=(s:string)=>root.querySelector(s) as HTMLElement;
 const shuffle=()=>{let a=[0,1,2,3,4,5,6];for(let i=6;i;i--){let j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]]}bag.push(...a)};
 const take=()=>{if(!bag.length)shuffle();return bag.shift()!};const fill=()=>{while(queue.length<4)queue.push(take())};
 const make=(k:number):P=>({c:SHAPES[k].map(v=>[...v]),x:4,y:k?2:1,k});
 const blocked=(q:P,dx=0,dy=0,c=q.c)=>c.some(([x,y])=>{const bx=q.x+x+dx,by=q.y+y+dy;return bx<0||bx>=C||by>=R||(by>=0&&board[by][bx])});
 const ui=()=>{$(".mode").textContent=mode;$(".score").textContent=score.toLocaleString();$(".lines").textContent=`${lines} lines · ${combo>0?combo+1+"× combo":"no combo"}`;$(".challengeState").textContent=lines>=4?"Complete ✓":`${lines} / 4`;$(".held").textContent=held===null?"—":"IJZLOST"[held];$(".next").textContent=queue.slice(0,3).map(k=>"IJZLOST"[k]).join("  ");let b=best[mode];$(".best").textContent=b?(mode==="Sprint"?(b/1000).toFixed(1)+"s":b.toLocaleString()):"—"};
 const spawn=()=>{fill();p=make(queue.shift()!);fill();canHold=true;if(blocked(p))finish(false);ui()};
 const finish=(win:boolean)=>{if(over)return;over=true;let result=mode==="Sprint"?Math.round(elapsed):score;if(mode==="Sprint"){if(win&&(!best[mode]||result<best[mode]))best[mode]=result}else best[mode]=Math.max(best[mode],result);$(".result").textContent=win?"CHALLENGE CLEARED":"STACK REKT";$(".final").textContent=`${score.toLocaleString()} points · ${lines} lines`;$(".overlay").classList.add("show");ui()};
 const lock=()=>{p.c.forEach(([x,y])=>{if(p.y+y>=0)board[p.y+y][p.x+x]=p.k+1});let full:number[]=[];for(let y=0;y<R;y++)if(board[y].every(Boolean))full.push(y);if(full.length){for(let i=full.length-1;i>=0;i--){board.splice(full[i],1);board.unshift(Array(C).fill(0))}combo++;lines+=full.length;score+=([0,100,300,500,800][full.length]+Math.max(0,combo)*75)*(1+Math.floor(lines/10))}else combo=-1;ui();if((mode==="Sprint"&&lines>=20)||(mode==="Marathon"&&lines>=40))finish(true);else spawn()};
 const move=(dx:number,dy:number)=>{if(over||blocked(p,dx,dy)){if(dy>0&&!over)lock();return false}p.x+=dx;p.y+=dy;return true};
 const rotate=()=>{if(over)return;const c=p.c.map(([x,y])=>[-y,x]);for(const k of [0,-1,1,-2,2])if(!blocked(p,k,0,c)){p.c=c;p.x+=k;break}};
 const drop=()=>{if(over)return;let d=0;while(!blocked(p,0,1)){p.y++;d++}score+=d*2;lock()};
 const hold=()=>{if(over||!canHold)return;const k=p.k;if(held===null){held=k;spawn()}else{const h=held;held=k;p=make(h);if(blocked(p))finish(false)}canHold=false;ui()};
 const reset=()=>{board.splice(0,R,...Array.from({length:R},()=>Array(C).fill(0)));bag=[];queue=[];held=null;score=lines=0;combo=-1;over=false;elapsed=fall=0;start=performance.now();last=start;$(".overlay").classList.remove("show");fill();spawn();ui()};
 const action=(a:string)=>{if(a==="left")move(-1,0);if(a==="right")move(1,0);if(a==="rotate")rotate();if(a==="down"){if(move(0,1))score++;ui()}if(a==="drop")drop();if(a==="hold")hold()};
 const addBlock=(x:number,y:number,c:number,ghost=false)=>{if(y<0)return;const m=new THREE.Mesh(boxGeo,ghost?ghostMat:mats[c-1]);m.position.set(x-C/2+.5,R/2-y-.5,ghost?.05:0);if(!ghost){m.castShadow=true;m.receiveShadow=true}blocks.add(m)};
 const draw=()=>{while(blocks.children.length)blocks.remove(blocks.children[0]);board.forEach((row,y)=>row.forEach((v,x)=>v&&addBlock(x,y,v)));if(!over){let gy=p.y;while(!blocked({...p,y:gy},0,1))gy++;p.c.forEach(([x,y])=>addBlock(p.x+x,gy+y,p.k+1,true));p.c.forEach(([x,y])=>addBlock(p.x+x,p.y+y,p.k+1))}};
 const resize=()=>{const w=host.clientWidth,h=host.clientHeight;renderer.setSize(w,h,false);camera.aspect=w/h;camera.updateProjectionMatrix()};
 const loop=(now:number)=>{const dt=Math.min(50,now-last);last=now;if(!over){elapsed=now-start;if(mode==="Score Attack"&&elapsed>=120000)finish(true);fall+=dt;const speed=Math.max(80,700-Math.floor(lines/10)*60);if(fall>=speed){fall=0;move(0,1)}$(".time").textContent=mode==="Score Attack"?Math.max(0,120-elapsed/1000).toFixed(1):(elapsed/1000).toFixed(1)}draw();renderer.render(scene,camera);raf=requestAnimationFrame(loop)};
 const key=(e:KeyboardEvent)=>{const m:Record<string,string>={ArrowLeft:"left",ArrowRight:"right",ArrowDown:"down",ArrowUp:"rotate"," ":"drop",c:"hold",C:"hold"};if(e.key.toLowerCase()==="r")reset();else if(m[e.key]){e.preventDefault();action(m[e.key])}};
 const click=(e:Event)=>{const t=e.target as HTMLElement,m=t.closest("[data-mode]") as HTMLElement,a=t.closest("[data-act]") as HTMLElement;if(m){mode=m.dataset.mode as Mode;reset()}else if(t.closest(".restart"))reset();else if(a)action(a.dataset.act!)};
 const down=(e:PointerEvent)=>{tx=e.clientX;ty=e.clientY};const up=(e:PointerEvent)=>{const dx=e.clientX-tx,dy=e.clientY-ty;if(Math.max(Math.abs(dx),Math.abs(dy))<18)rotate();else if(Math.abs(dx)>Math.abs(dy))action(dx>0?"right":"left");else action(dy>60?"drop":dy>0?"down":"rotate")};
 const ro=new ResizeObserver(resize);ro.observe(host);root.addEventListener("click",click);renderer.domElement.addEventListener("pointerdown",down);renderer.domElement.addEventListener("pointerup",up);window.addEventListener("keydown",key);resize();reset();raf=requestAnimationFrame(loop);
 return()=>{cancelAnimationFrame(raf);ro.disconnect();root.removeEventListener("click",click);renderer.domElement.removeEventListener("pointerdown",down);renderer.domElement.removeEventListener("pointerup",up);window.removeEventListener("keydown",key);scene.traverse(o=>{const m=o as THREE.Mesh;if(m.geometry)m.geometry.dispose();if(m.material){const a=Array.isArray(m.material)?m.material:[m.material];a.forEach(x=>x.dispose())}});renderer.dispose();renderer.domElement.remove();if(typeof dispose==="function")dispose()};
}