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

const MAZES=[
["###################","#........#........#","#.###.##.#.##.###.#","#o###.##.#.##.###o#","#.................#","#.###.#.#####.#.###","#.....#...#...#...#","#####.### # ###.###","    #.#       #.#  ","#####.# ## ## #.###","     .  #   #  .   ","#####.# ##### #.###","    #.#       #.#  ","#####.# ##### #.###","#........#........#","#.###.##.#.##.###.#","#o..#.... P ....#o#","###.#.#.#####.#.#.#","#.....#...#...#...#","#.#######.#.#######","#.................#","###################"],
["###################","#o.......#.......o#","#.###.##.#.##.###.#","#.....##...##.....#","###.#.#######.#.###","#...#....#....#...#","#.#####..#..#####.#","#.......###.......#","###.###     ###.###","   .#  ## ##  #.   ","....#  #   #  #....","   .#  #####  #.   ","###.###     ###.###","#.......###.......#","#.#####..#..#####.#","#...#....#....#...#","###.#... P ...#.###","#.....##...##.....#","#.###.##.#.##.###.#","#o.......#.......o#","#.................#","###################"],
["###################","#o....#.....#....o#","#.##.#.###.#.#.##.#","#....#.....#.#....#","####.###.#.###.####","#......#.#.#......#","#.####.#.#.#.####.#","#.#....#...#....#.#","#.#.##       ##.#.#","  ..#  ## ##  #..  ","....#  #   #  #....","  ..#  #####  #..  ","#.#.##       ##.#.#","#.#....#...#....#.#","#.####.#.#.#.####.#","#......#.#.#......#","####.##..P..##.####","#....#.#####.#....#","#.##.#.......#.##.#","#o...###...###...o#","#.................#","###################"]];
const COLS=19,ROWS=22,DIRS=[{x:1,y:0},{x:0,y:1},{x:-1,y:0},{x:0,y:-1}];
type Actor={x:number;y:number;dir:number;next:number;homeX:number;homeY:number;kind:number;mesh:THREE.Group};

export default function mount(){
 const root=document.querySelector("#game-root") as HTMLElement;if(!root)return()=>{};
 render(<div style="min-height:100%;display:flex;align-items:center;justify-content:center;background:radial-gradient(circle,#11113b,#020209 70%);color:#fff;font-family:monospace;padding:12px;box-sizing:border-box"><div style="width:min(100%,720px);text-align:center"><div style="display:grid;grid-template-columns:1fr auto 1fr;gap:8px;align-items:end"><span id="score" style="text-align:left">SCORE 000000</span><b style="color:#ffe229;font-size:20px">MAZE MUNCH 3D</b><span id="high" style="text-align:right;color:#70ffff">HI 000000</span></div><div style="display:flex;justify-content:space-between;color:#ffb85c;margin:5px 0"><span id="round">MAZE 1 / 3</span><span id="lives">LIVES 3</span></div><div id="stage" style="position:relative;width:100%;aspect-ratio:19/18;background:#000;border:3px solid #3434cd;box-shadow:0 0 28px #19199a;touch-action:none;overflow:hidden"><div id="banner" style="position:absolute;z-index:2;left:0;right:0;top:43%;font:bold 28px monospace;color:#7effff;text-shadow:2px 2px #15158e;pointer-events:none"></div></div><div id="message" style="height:24px;margin-top:7px;color:#65ffff">Clear every maze. Extra life at 10,000!</div><button id="restart" style="font:700 16px monospace;color:#080816;background:#ffe229;border:0;border-radius:5px;padding:9px 22px;cursor:pointer">RESTART / RETRY (R)</button><div style="margin-top:8px;color:#aaa;font-size:12px">Move: Arrows / WASD · Swipe or tap · Power pellets frighten ghosts</div></div></div>,root);
 const stage=root.querySelector("#stage") as HTMLElement,banner=root.querySelector("#banner") as HTMLElement,message=root.querySelector("#message") as HTMLElement,restartBtn=root.querySelector("#restart") as HTMLButtonElement;
 const scoreEl=root.querySelector("#score") as HTMLElement,highEl=root.querySelector("#high") as HTMLElement,livesEl=root.querySelector("#lives") as HTMLElement,roundEl=root.querySelector("#round") as HTMLElement;
 const renderer=new THREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(Math.min(devicePixelRatio,2));renderer.setSize(stage.clientWidth,stage.clientHeight);renderer.shadowMap.enabled=true;renderer.domElement.style.cssText="display:block;width:100%;height:100%";stage.prepend(renderer.domElement);
 const scene=new THREE.Scene();scene.background=new THREE.Color(0x020208);scene.fog=new THREE.Fog(0x020208,20,35);
 const camera=new THREE.PerspectiveCamera(38,stage.clientWidth/stage.clientHeight,.1,100);camera.position.set(0,20,18);camera.lookAt(0,0,0);
 scene.add(new THREE.HemisphereLight(0x7777ff,0x080818,1.3));const sun=new THREE.DirectionalLight(0xffffff,1.8);sun.position.set(-7,14,9);sun.castShadow=true;scene.add(sun);
 const board=new THREE.Group();scene.add(board);const floor=new THREE.Mesh(new THREE.PlaneGeometry(COLS+2,ROWS+2),new THREE.MeshStandardMaterial({color:0x03030b,roughness:.7,metalness:.2}));floor.rotation.x=-Math.PI/2;floor.position.y=-.15;floor.receiveShadow=true;board.add(floor);
 let grid:string[][]=[],pellets:(THREE.Mesh|null)[][]=[],hero:Actor,ghosts:Actor[]=[];let score=0,high=0,lives=3,level=0,remaining=0,power=0,chain=0,state="play",freeze=0,last=0,raf=0,bannerTime=0,scatter=0,extraAt=10000,startX=0,startY=0;let audio:AudioContext|null=null;
 const mats:THREE.Material[]=[];const geo:THREE.BufferGeometry[]=[];const mat=(p:THREE.Material)=>{mats.push(p);return p};const geometry=<T extends THREE.BufferGeometry>(g:T)=>{geo.push(g);return g};
 const pos=(x:number,y:number,m:THREE.Object3D)=>m.position.set(x-(COLS-1)/2,.25,y-(ROWS-1)/2);
 const beep=(f:number,d=.05)=>{try{audio||=new AudioContext();const o=audio.createOscillator(),g=audio.createGain();o.type="square";o.frequency.value=f;g.gain.setValueAtTime(.035,audio.currentTime);g.gain.exponentialRampToValueAtTime(.001,audio.currentTime+d);o.connect(g);g.connect(audio.destination);o.start();o.stop(audio.currentTime+d)}catch{}};
 const hud=()=>{high=Math.max(high,score);scoreEl.textContent=`SCORE ${String(score).padStart(6,"0")}`;highEl.textContent=`HI ${String(high).padStart(6,"0")}`;livesEl.textContent=`LIVES ${lives}`;roundEl.textContent=`MAZE ${level+1} / ${MAZES.length}`};
 const makeHero=()=>{const g=new THREE.Group(),body=new THREE.Mesh(geometry(new THREE.SphereGeometry(.39,20,16)),mat(new THREE.MeshStandardMaterial({color:0xffe229,emissive:0x554400})));body.castShadow=true;g.add(body);board.add(g);return g};
 const makeGhost=(color:number)=>{const g=new THREE.Group(),body=new THREE.Mesh(geometry(new THREE.SphereGeometry(.4,18,12,0,Math.PI*2,0,Math.PI*.72)),mat(new THREE.MeshStandardMaterial({color,emissive:new THREE.Color(color).multiplyScalar(.2)})));body.position.y=.12;g.add(body);const skirt=new THREE.Mesh(geometry(new THREE.CylinderGeometry(.4,.44,.35,12)),body.material);skirt.position.y=-.17;g.add(skirt);for(const x of [-.15,.15]){const eye=new THREE.Mesh(geometry(new THREE.SphereGeometry(.09,10,8)),mat(new THREE.MeshBasicMaterial({color:0xffffff})));eye.position.set(x,.23,.34);g.add(eye)}g.children.forEach(o=>{if(o instanceof THREE.Mesh)o.castShadow=true});board.add(g);return g};
 function clearBoard(){[...board.children].forEach(o=>{if(o!==floor)board.remove(o)})}
 function actors(){hero={x:9,y:16,dir:2,next:2,homeX:9,homeY:16,kind:0,mesh:makeHero()};pos(hero.x,hero.y,hero.mesh);const p=[[8,10],[9,10],[10,10],[9,8]],colors=[0xff3344,0xff77cc,0x43eaff,0xff9838];ghosts=p.map((q,i)=>{const a={x:q[0],y:q[1],dir:i%2?0:2,next:0,homeX:q[0],homeY:q[1],kind:i,mesh:makeGhost(colors[i])};pos(a.x,a.y,a.mesh);return a})}
 function loadLevel(){clearBoard();grid=MAZES[level].map(r=>r.padEnd(COLS," ").split(""));pellets=Array.from({length:ROWS},()=>Array(COLS).fill(null));remaining=0;const hue=[0x2525c8,0x8c28c8,0x10a985][level];for(let r=0;r<ROWS;r++)for(let c=0;c<COLS;c++){const cell=grid[r][c];if(cell==="#"){const wall=new THREE.Mesh(geometry(new THREE.BoxGeometry(.92,.72,.92)),mat(new THREE.MeshStandardMaterial({color:hue,emissive:new THREE.Color(hue).multiplyScalar(.25),roughness:.3,metalness:.25})));pos(c,r,wall);wall.position.y=.22;wall.castShadow=true;wall.receiveShadow=true;board.add(wall)}else if(cell==="."||cell==="o"){remaining++;const p=new THREE.Mesh(geometry(new THREE.SphereGeometry(cell==="o"?.18:.07,10,8)),mat(new THREE.MeshBasicMaterial({color:0xffd7a6})));pos(c,r,p);p.position.y=.17;board.add(p);pellets[r][c]=p}}power=chain=scatter=0;state="play";freeze=1;actors();banner.textContent=`MAZE ${level+1}`;bannerTime=1.3;message.textContent=`Round speed ${level+1} · ${remaining} pellets remain`;hud()}
 function resetActors(){board.remove(hero.mesh);ghosts.forEach(g=>board.remove(g.mesh));actors()}
 function reset(){score=0;lives=3;level=0;extraAt=10000;loadLevel()}
 const wall=(x:number,y:number)=>{const c=Math.round(x),r=Math.round(y);return r<0||r>=ROWS||(c>=0&&c<COLS&&grid[r][c]==="#")};
 const can=(a:Actor,d:number,n=.11)=>{const q=DIRS[d],x=a.x+q.x*n,y=a.y+q.y*n,p=.3;return!wall(x-p,y-p)&&!wall(x+p,y-p)&&!wall(x-p,y+p)&&!wall(x+p,y+p)};
 const centered=(a:Actor)=>Math.abs(a.x-Math.round(a.x))<.09&&Math.abs(a.y-Math.round(a.y))<.09;
 const wrap=(a:Actor)=>{if(a.x<-.7)a.x=COLS-.3;if(a.x>COLS-.3)a.x=-.7};
 function award(n:number){score+=n;if(score>=extraAt){lives++;extraAt+=15000;banner.textContent="EXTRA LIFE!";bannerTime=1.2;beep(1040,.3)}hud()}
 function target(g:Actor){if(power)return{x:g.x+(g.x-hero.x)*3,y:g.y+(g.y-hero.y)*3};if(scatter%13<3)return[{x:COLS,y:0},{x:0,y:0},{x:COLS,y:ROWS},{x:0,y:ROWS}][g.kind];if(g.kind===0)return hero;if(g.kind===1)return{x:hero.x+DIRS[hero.dir].x*5,y:hero.y+DIRS[hero.dir].y*5};if(g.kind===2)return{x:hero.x*2-ghosts[0].x,y:hero.y*2-ghosts[0].y};return Math.hypot(g.x-hero.x,g.y-hero.y)<6?{x:1,y:ROWS-1}:hero}
 function update(dt:number){if(bannerTime>0&&(bannerTime-=dt)<=0)banner.textContent="";if(state!=="play")return;if(freeze>0){freeze-=dt;return}power=Math.max(0,power-dt);scatter+=dt;const hs=(5.5+level*.4)*dt;if(can(hero,hero.next,hs)&&(hero.next===(hero.dir+2)%4||centered(hero))){hero.x=Math.round(hero.x);hero.y=Math.round(hero.y);hero.dir=hero.next}if(can(hero,hero.dir,hs)){hero.x+=DIRS[hero.dir].x*hs;hero.y+=DIRS[hero.dir].y*hs}wrap(hero);pos(hero.x,hero.y,hero.mesh);hero.mesh.rotation.y=-hero.dir*Math.PI/2;
  const c=Math.round(hero.x),r=Math.round(hero.y);if(r>=0&&r<ROWS&&c>=0&&c<COLS&&Math.hypot(hero.x-c,hero.y-r)<.36){const cell=grid[r][c];if(cell==="."||cell==="o"){grid[r][c]=" ";remaining--;const p=pellets[r][c];if(p){board.remove(p);pellets[r][c]=null}award(cell==="o"?50:10);beep(cell==="o"?180:440,.035);if(cell==="o"){power=Math.max(4.5,7-level*.8);chain=0;ghosts.forEach(g=>g.dir=(g.dir+2)%4);message.textContent="POWER MODE — hunt the ghosts!"}if(!remaining){award(lives*500);if(level<MAZES.length-1){state="between";freeze=1.7;banner.textContent="MAZE CLEAR!";message.textContent="Next maze approaching…"}else{state="win";banner.textContent="ARCADE MASTER!";message.textContent="ALL MAZES CLEARED — arcade champion!";beep(1100,.5)}}}}
  ghosts.forEach((g,i)=>{const s=(power?3:3.8+level*.35+i*.06)*dt;if(centered(g)){g.x=Math.round(g.x);g.y=Math.round(g.y);let opts=[0,1,2,3].filter(d=>d!==(g.dir+2)%4&&can(g,d,s+.03));if(!opts.length)opts=[(g.dir+2)%4];const t=target(g);opts.sort((a,b)=>Math.hypot(g.x+DIRS[a].x-t.x,g.y+DIRS[a].y-t.y)-Math.hypot(g.x+DIRS[b].x-t.x,g.y+DIRS[b].y-t.y)+(Math.random()-.5)*(power?3:.25));g.dir=opts[0]}if(can(g,g.dir,s)){g.x+=DIRS[g.dir].x*s;g.y+=DIRS[g.dir].y*s}wrap(g);pos(g.x,g.y,g.mesh);const frightened=power>0;g.mesh.scale.setScalar(frightened?.88:1);g.mesh.children.slice(0,2).forEach(o=>{const m=(o as THREE.Mesh).material as THREE.MeshStandardMaterial;if(m.emissive)m.emissive.set(frightened?0x0000aa:0x000000)});if(Math.hypot(g.x-hero.x,g.y-hero.y)<.58){if(power){const gain=200*Math.pow(2,Math.min(chain++,3));award(gain);g.x=g.homeX;g.y=g.homeY;banner.textContent=`${gain} ×${chain}`;bannerTime=.7;beep(350+chain*130,.12)}else{lives--;hud();beep(70,.35);if(lives<=0){state="lose";banner.textContent="GAME OVER";message.textContent="GAME OVER — Retry and protect the high score!"}else{resetActors();power=chain=0;freeze=.8;message.textContent=`Caught! ${lives} ${lives===1?"life":"lives"} left`}}}})}
 function frame(now:number){const dt=Math.min(.033,(now-last)/1000||0);last=now;update(dt);if(state==="between"&&freeze<=0){level++;loadLevel()}const pulse=1+Math.sin(now/110)*.18;for(let r=0;r<ROWS;r++)for(let c=0;c<COLS;c++)if(grid[r]?.[c]==="o"&&pellets[r][c])pellets[r][c]!.scale.setScalar(pulse);renderer.render(scene,camera);raf=requestAnimationFrame(frame)}
 const request=(d:number)=>{if(state==="play")hero.next=d};
 const keydown=(e:KeyboardEvent)=>{const keys:Record<string,number>={ArrowRight:0,d:0,D:0,ArrowDown:1,s:1,S:1,ArrowLeft:2,a:2,A:2,ArrowUp:3,w:3,W:3};if(e.key in keys){e.preventDefault();request(keys[e.key])}if(e.key.toLowerCase()==="r")reset();else if(e.key==="Enter"&&state!=="play")reset()};
 const down=(e:PointerEvent)=>{startX=e.clientX;startY=e.clientY;renderer.domElement.setPointerCapture(e.pointerId)};
 const up=(e:PointerEvent)=>{if(state!=="play"){reset();return}const dx=e.clientX-startX,dy=e.clientY-startY;if(Math.hypot(dx,dy)>12)request(Math.abs(dx)>Math.abs(dy)?(dx>0?0:2):(dy>0?1:3));else{const q=renderer.domElement.getBoundingClientRect(),x=(e.clientX-q.left)/q.width*COLS,y=(e.clientY-q.top)/q.height*ROWS;request(Math.abs(x-hero.x)>Math.abs(y-hero.y)?(x>hero.x?0:2):(y>hero.y?1:3))}};
 const resize=()=>{const w=stage.clientWidth,h=stage.clientHeight;renderer.setSize(w,h,false);camera.aspect=w/h;camera.updateProjectionMatrix()};
 window.addEventListener("keydown",keydown);window.addEventListener("resize",resize);renderer.domElement.addEventListener("pointerdown",down);renderer.domElement.addEventListener("pointerup",up);restartBtn.addEventListener("click",reset);reset();raf=requestAnimationFrame(frame);
 return()=>{cancelAnimationFrame(raf);window.removeEventListener("keydown",keydown);window.removeEventListener("resize",resize);renderer.domElement.removeEventListener("pointerdown",down);renderer.domElement.removeEventListener("pointerup",up);restartBtn.removeEventListener("click",reset);audio?.close();geo.forEach(g=>g.dispose());mats.forEach(m=>m.dispose());renderer.dispose();renderer.domElement.remove();render(null,root)};
}