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

type Route = { name: string; length: number; sky: number; fog: number; wind: number; difficulty: number };
type Racer = { x: number; y: number; z: number; vy: number; speed: number; skill: number; phase: number; bird: THREE.Group };
type Hazard = { x: number; y: number; z: number; r: number; drift: number; mesh: THREE.Group };
type Gate = { x: number; y: number; z: number; taken: boolean; shortcut: boolean; mesh: THREE.Group };
type Current = { x: number; y: number; z: number; force: number; mesh: THREE.Group };

const routes: Route[] = [
  { name: "Sunrise Sprint", length: 7200, sky: 0x55bde9, fog: 0xbcecff, wind: 55, difficulty: 1 },
  { name: "Thunder Run", length: 9000, sky: 0x26355f, fog: 0x7180a3, wind: 90, difficulty: 1.45 },
  { name: "Dragon Pass", length: 10800, sky: 0x7c397a, fog: 0xe88790, wind: 125, difficulty: 1.9 }
];

export default function mount() {
  const root = document.getElementById("game-root");
  if (!root) return () => {};
  const shell = document.createElement("div");
  shell.style.cssText = "position:relative;width:100%;height:100%;min-height:520px;overflow:hidden;background:#14233f;color:white;font-family:system-ui,sans-serif;touch-action:none;user-select:none";
  const stage = document.createElement("div");
  stage.style.cssText = "position:absolute;inset:0";
  const hud = document.createElement("div");
  hud.style.cssText = "position:absolute;left:12px;top:12px;padding:10px 13px;min-width:245px;border-radius:12px;background:#07172cdd;font-weight:800;line-height:1.45;pointer-events:none;text-shadow:0 1px #000";
  const controls = document.createElement("div");
  controls.style.cssText = "position:absolute;right:12px;top:12px;display:flex;gap:7px";
  const select = document.createElement("select");
  select.setAttribute("aria-label", "Select course");
  select.style.cssText = "padding:9px;border:0;border-radius:9px;background:white;color:#17345b;font-weight:800";
  routes.forEach((r, i) => { const o = document.createElement("option"); o.value = String(i); o.textContent = r.name; select.append(o); });
  const restart = document.createElement("button");
  restart.textContent = "Restart";
  restart.style.cssText = "padding:10px 14px;border:0;border-radius:9px;background:#ffe45d;color:#25314c;font-weight:900;cursor:pointer";
  controls.append(select, restart);
  const help = document.createElement("div");
  help.textContent = "WASD / arrows steer • Space or hold/tap flaps • Shift or double-tap boosts • Cyan tunnels recharge boost";
  help.style.cssText = "position:absolute;left:50%;bottom:14px;transform:translateX(-50%);padding:7px 12px;border-radius:18px;background:#07172cbd;font-size:12px;white-space:nowrap;pointer-events:none";
  const banner = document.createElement("div");
  banner.style.cssText = "position:absolute;left:50%;top:20%;transform:translate(-50%,-50%);font-size:26px;font-weight:1000;text-align:center;text-shadow:0 3px 8px #001;opacity:0;transition:opacity .15s;pointer-events:none";
  const end = document.createElement("div");
  end.style.cssText = "display:none;position:absolute;inset:0;align-items:center;justify-content:center;flex-direction:column;text-align:center;background:#061326c9;font-size:32px;font-weight:1000;pointer-events:none;text-shadow:0 2px 6px #001";
  shell.append(stage, hud, controls, help, banner, end);
  root.replaceChildren(shell);
  render(null, root);

  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(58, 1, .1, 1200);
  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio(Math.min(devicePixelRatio || 1, 2));
  renderer.shadowMap.enabled = true;
  renderer.outputColorSpace = THREE.SRGBColorSpace;
  renderer.domElement.style.cssText = "display:block;width:100%;height:100%";
  stage.append(renderer.domElement);
  scene.add(new THREE.HemisphereLight(0xffffff, 0x334466, 2.2));
  const sun = new THREE.DirectionalLight(0xfff1c7, 2.5);
  sun.position.set(-20, 40, 20); sun.castShadow = true; scene.add(sun);
  const world = new THREE.Group(); scene.add(world);
  const keys = new Set<string>();
  const best: Record<number, number> = {};
  let raf = 0, last = performance.now(), elapsed = 0, score = 0, boost = 45, boostTime = 0, shortcut = 0, notice = 0;
  let routeIndex = 0, route = routes[0], state: "play" | "win" | "lose" = "play", pointer = false, lastTap = 0;
  let rivals: Racer[] = [], hazards: Hazard[] = [], gates: Gate[] = [], currents: Current[] = [];
  let audio: AudioContext | null = null;
  const player = { x: 0, y: 0, z: 0, vy: 0, vz: 0, speed: 260, stamina: 100, bird: new THREE.Group() };

  function material(color: number, emissive = 0) { return new THREE.MeshStandardMaterial({ color, emissive, roughness: .65 }); }
  function makeBird(color: number) {
    const g = new THREE.Group();
    const body = new THREE.Mesh(new THREE.SphereGeometry(1, 14, 10), material(color)); body.scale.set(1.8, .85, .8); body.castShadow = true; g.add(body);
    const beak = new THREE.Mesh(new THREE.ConeGeometry(.35, 1.1, 4), material(0xffd43b)); beak.rotation.z = -Math.PI / 2; beak.position.x = 2; g.add(beak);
    const wingGeo = new THREE.ConeGeometry(.72, 2.5, 3);
    for (const side of [-1, 1]) { const wing = new THREE.Mesh(wingGeo, material(0xfff0b0)); wing.name = "wing"; wing.position.z = side * .72; wing.rotation.x = side * 1.15; wing.rotation.z = -.25; g.add(wing); }
    const eye = new THREE.Mesh(new THREE.SphereGeometry(.13, 8, 6), material(0x101828)); eye.position.set(1.22, .4, .65); g.add(eye);
    g.scale.setScalar(1.35); return g;
  }
  function disposeGroup(g: THREE.Object3D) { g.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()); } }); }
  function tone(f: number, duration = .1) { try { audio ||= new AudioContext(); if (audio.state === "suspended") audio.resume(); const o = audio.createOscillator(), gain = audio.createGain(); o.frequency.value = f; gain.gain.setValueAtTime(.04, audio.currentTime); gain.gain.exponentialRampToValueAtTime(.001, audio.currentTime + duration); o.connect(gain); gain.connect(audio.destination); o.start(); o.stop(audio.currentTime + duration); } catch {} }
  function say(s: string, color = "white") { banner.textContent = s; banner.style.color = color; banner.style.opacity = "1"; notice = 1.1; }
  function bar(n: number, color: string) { return `<span style="display:inline-block;width:112px;height:8px;background:#ffffff44;border-radius:6px;overflow:hidden"><i style="display:block;width:${Math.max(0, n)}%;height:100%;background:${color}"></i></span>`; }
  function clearWorld() { while (world.children.length) { const child = world.children.pop()!; disposeGroup(child); } }

  function makeCloud(x: number, y: number, z: number, r: number) {
    const g = new THREE.Group();
    [[0,0,0,1], [r*.65,0,0,r*.72], [-r*.65,0,0,r*.68], [0,r*.35,0,r*.78]].forEach(p => { const m = new THREE.Mesh(new THREE.IcosahedronGeometry(p[3] as number, 1), material(0x29354d, 0x101624)); m.position.set(p[0] as number, p[1] as number, p[2] as number); g.add(m); });
    g.position.set(x, y, z); world.add(g); return g;
  }
  function makeGate(x: number, y: number, z: number, shortcutGate: boolean) {
    const g = new THREE.Group(), color = shortcutGate ? 0x55ffff : 0xffdf45;
    const ring = new THREE.Mesh(new THREE.TorusGeometry(shortcutGate ? 7 : 5, .65, 10, 28), material(color, color)); ring.rotation.y = Math.PI / 2; g.add(ring);
    if (shortcutGate) { const top = new THREE.Mesh(new THREE.ConeGeometry(1, 3, 4), material(0xffffff, 0x55ffff)); top.position.y = 9; top.rotation.z = Math.PI; g.add(top); }
    g.position.set(x, y, z); world.add(g); return g;
  }
  function makeCurrent(x: number, y: number, z: number) {
    const g = new THREE.Group();
    for (let i = 0; i < 5; i++) { const ring = new THREE.Mesh(new THREE.TorusGeometry(8, .16, 6, 24), new THREE.MeshBasicMaterial({ color: 0x70ffff, transparent: true, opacity: .45 })); ring.rotation.y = Math.PI / 2; ring.position.x = i * 10; g.add(ring); }
    g.position.set(x, y, z); world.add(g); return g;
  }

  function reset() {
    clearWorld(); routeIndex = Number(select.value); route = routes[routeIndex];
    scene.background = new THREE.Color(route.sky); scene.fog = new THREE.Fog(route.fog, 80, 520);
    Object.assign(player, { x: 0, y: 0, z: 0, vy: 0, vz: 0, speed: 260, stamina: 100 });
    player.bird = makeBird(0x1875dc); world.add(player.bird);
    elapsed = score = boostTime = shortcut = 0; boost = 45; state = "play"; pointer = false;
    rivals = [0xff5277, 0xa468f0, 0x20c18a, 0xff9d32].map((color, i) => { const bird = makeBird(color); world.add(bird); return { x: 80 + i * 42, y: -8 + i * 5, z: -10 + i * 7, vy: 0, speed: 243 + route.difficulty * 11 + i * 3, skill: .5 + i * .14, phase: i * 1.7, bird }; });
    hazards = []; gates = []; currents = [];
    for (let x = 500, i = 0; x < route.length - 250; x += 380, i++) {
      const y = -14 + (i * 11 % 29), z = -20 + (i * 17 % 41);
      if (i % 3 === 1) { const r = 6 + i % 3; hazards.push({ x, y, z, r, drift: (i % 2 ? 1 : -1) * (3 + route.difficulty), mesh: makeCloud(x, y, z, r) }); }
      else { const shortcutGate = i > 3 && i % 5 === 0; gates.push({ x, y, z, taken: false, shortcut: shortcutGate, mesh: makeGate(x, y, z, shortcutGate) }); }
      if (i % 4 === 2) currents.push({ x: x + 100, y: y * .5, z: z * .5, force: route.wind, mesh: makeCurrent(x + 100, y * .5, z * .5) });
    }
    const finish = new THREE.Group();
    for (let i = -4; i <= 4; i++) { const cube = new THREE.Mesh(new THREE.BoxGeometry(.8, 5, 5), material(i % 2 ? 0xffffff : 0x182554)); cube.position.set(route.length, i * 5, 0); finish.add(cube); } world.add(finish);
    end.style.display = "none"; restart.textContent = "Restart"; say(route.name, "#ffe86b"); last = performance.now();
  }
  function finish(win: boolean, reason: string) {
    if (state !== "play") return; state = win ? "win" : "lose";
    if (win && (!best[routeIndex] || elapsed < best[routeIndex])) best[routeIndex] = elapsed;
    end.innerHTML = `<div>${win ? "🏆 Victory!" : "⛈ Race Over"}</div><div style="font-size:16px;margin-top:10px">${reason}<br>Score: ${Math.round(score)}${best[routeIndex] ? `<br>Best: ${best[routeIndex].toFixed(1)}s` : ""}<br><span style="opacity:.75">Press R, Enter, Space, or Retry</span></div>`;
    end.style.display = "flex"; restart.textContent = "Retry"; tone(win ? 720 : 110, .4);
  }
  function boostNow() { if (state !== "play" || boost < 25 || boostTime > 0) return; boost -= 25; boostTime = 1.15; say("TAILWIND BOOST!", "#70ffff"); tone(190, .18); }

  function update(dt: number) {
    notice -= dt; if (notice < 0) banner.style.opacity = "0";
    if (state !== "play") return;
    elapsed += dt; boostTime = Math.max(0, boostTime - dt); shortcut = Math.max(0, shortcut - dt);
    const up = keys.has("ArrowUp") || keys.has("KeyW") || keys.has("Space") || pointer;
    const down = keys.has("ArrowDown") || keys.has("KeyS"), left = keys.has("ArrowLeft") || keys.has("KeyA"), right = keys.has("ArrowRight") || keys.has("KeyD");
    let inCurrent = false;
    currents.forEach(c => { if (Math.abs(player.x - c.x - 20) < 28 && Math.hypot(player.y - c.y, player.z - c.z) < 9) inCurrent = true; c.mesh.children.forEach((r, i) => r.rotation.x = elapsed * 1.5 + i); });
    player.vy += -4 * dt; if (up && player.stamina > 0) { player.vy += 24 * dt; player.stamina -= 15 * dt; } else player.stamina = Math.min(100, player.stamina + 9 * dt);
    if (down) player.vy -= 18 * dt; player.vz += ((right ? 17 : 0) - (left ? 17 : 0)) * dt;
    player.vy *= Math.pow(.12, dt); player.vz *= Math.pow(.08, dt); player.y += player.vy * dt; player.z += player.vz * dt;
    const desired = 260 + (inCurrent ? 35 : 0) + (boostTime ? 145 : 0) + (shortcut ? 70 : 0);
    player.speed += (desired - player.speed) * Math.min(1, dt * 4); player.x += player.speed * dt; score += player.speed * dt * .12;
    if (inCurrent) { boost = Math.min(100, boost + 11 * dt); player.stamina = Math.min(100, player.stamina + 6 * dt); }
    hazards.forEach(h => { h.z += h.drift * dt; if (Math.abs(h.z) > 29) h.drift *= -1; h.mesh.position.z = h.z; h.mesh.rotation.x += dt * .25; if (Math.hypot(player.x - h.x, player.y - h.y, player.z - h.z) < h.r + 2.3) finish(false, "You struck a moving storm cloud."); });
    rivals.forEach((r, i) => { const targetY = Math.sin(elapsed * 1.2 + r.phase) * 12 + (i - 1.5) * 3; r.vy += (targetY - r.y) * dt * r.skill; r.vy *= Math.pow(.14, dt); r.y += r.vy * dt; r.z = Math.sin(elapsed * .8 + r.phase) * 18; r.x += (r.speed + Math.max(-25, Math.min(38, (player.x - r.x) * .025))) * dt; r.bird.position.set(r.x, r.y, r.z); });
    gates.forEach(g => { g.mesh.rotation.x += dt * .35; if (!g.taken && Math.abs(player.x - g.x) < 4 && Math.hypot(player.y - g.y, player.z - g.z) < (g.shortcut ? 7 : 5)) { g.taken = true; g.mesh.visible = false; boost = Math.min(100, boost + 22); score += g.shortcut ? 850 : 350; if (g.shortcut) shortcut = 4; say(g.shortcut ? "SHORTCUT!" : "RING + BOOST", "#ffef69"); tone(g.shortcut ? 650 : 490); } });
    if (Math.abs(player.y) > 34 || Math.abs(player.z) > 34) finish(false, "You left the safe sky corridor.");
    if (player.x >= route.length) { const place = 1 + rivals.filter(r => r.x > player.x).length; finish(place === 1, place === 1 ? `First place in ${elapsed.toFixed(1)} seconds!` : `Finished in position ${place}.`); }
    player.bird.position.set(player.x, player.y, player.z); player.bird.rotation.z = Math.max(-.4, Math.min(.4, player.vy / 18));
    player.bird.children.filter(x => x.name === "wing").forEach((w, i) => w.rotation.x = (i ? 1 : -1) * (1.05 + Math.sin(elapsed * 12) * .45));
  }

  function draw() {
    const lead = 24 + (boostTime ? 8 : 0);
    camera.position.lerp(new THREE.Vector3(player.x - lead, player.y + 8, player.z + 24), .08);
    camera.lookAt(player.x + 32, player.y, player.z);
    renderer.render(scene, camera);
    const place = 1 + rivals.filter(r => r.x > player.x).length, remaining = Math.ceil(Math.max(0, route.length - player.x) / 10);
    hud.innerHTML = `${route.name}　 Position ${place}/5<br>${remaining}m to finish　 ${elapsed.toFixed(1)}s<br>Score ${Math.round(score)}${best[routeIndex] ? `　 Best ${best[routeIndex].toFixed(1)}s` : ""}<br><span style="font-size:10px">STAMINA</span> ${bar(player.stamina, player.stamina < 25 ? "#ff5c64" : "#ffe34f")}<br><span style="font-size:10px">BOOST</span>　 ${bar(boost, "#5af5ff")}`;
  }
  function resize() { const b = shell.getBoundingClientRect(); renderer.setSize(Math.max(320, b.width), Math.max(420, b.height), false); camera.aspect = Math.max(320, b.width) / Math.max(420, b.height); camera.updateProjectionMatrix(); }
  function loop(now: number) { const dt = Math.min(.034, (now - last) / 1000); last = now; update(dt); draw(); raf = requestAnimationFrame(loop); }
  function keydown(e: KeyboardEvent) { if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Space"].includes(e.code)) e.preventDefault(); if (state !== "play" && ["KeyR", "Enter", "Space"].includes(e.code)) { reset(); return; } if ((e.code === "ShiftLeft" || e.code === "ShiftRight") && !e.repeat) boostNow(); keys.add(e.code); }
  const keyup = (e: KeyboardEvent) => keys.delete(e.code);
  const pointerDown = (e: PointerEvent) => { e.preventDefault(); const now = performance.now(); if (now - lastTap < 280) boostNow(); lastTap = now; pointer = true; };
  const pointerUp = () => { pointer = false; };
  window.addEventListener("resize", resize); window.addEventListener("keydown", keydown); window.addEventListener("keyup", keyup); renderer.domElement.addEventListener("pointerdown", pointerDown); window.addEventListener("pointerup", pointerUp); window.addEventListener("pointercancel", pointerUp); restart.addEventListener("click", reset); select.addEventListener("change", reset);
  resize(); reset(); raf = requestAnimationFrame(loop);
  return () => { cancelAnimationFrame(raf); window.removeEventListener("resize", resize); window.removeEventListener("keydown", keydown); window.removeEventListener("keyup", keyup); renderer.domElement.removeEventListener("pointerdown", pointerDown); window.removeEventListener("pointerup", pointerUp); window.removeEventListener("pointercancel", pointerUp); restart.removeEventListener("click", reset); select.removeEventListener("change", reset); clearWorld(); renderer.dispose(); if (audio) audio.close(); root.replaceChildren(); };
}