import * as THREE from 'three';
import './styles.css';

type Screen = 'menu' | 'levels' | 'garage' | 'race' | 'result';
type CameraMode = 'follow' | 'first';
type LevelId =
  | 'warmup'
  | 'compare'
  | 'hundred'
  | 'table'
  | 'missing'
  | 'negative'
  | 'squares'
  | 'percent'
  | 'fraction'
  | 'equation'
  | 'mix';

type Driver = {
  id: string;
  name: string;
  color: number;
  accent: number;
  price: number;
  owned: boolean;
};

type KartSkin = {
  id: string;
  name: string;
  body: number;
  trim: number;
  price: number;
  owned: boolean;
};

type SaveData = {
  diamonds: number;
  ownedDrivers: string[];
  ownedKarts: string[];
  selectedDriver: string;
  selectedKart: string;
};

type Question = {
  title: string;
  text: string;
  answers: string[];
  correct: string;
};

type Racer = {
  id: string;
  group: THREE.Group;
  lane: number;
  progress: number;
  speed: number;
  boost: number;
  botTimer: number;
  answerSkill: number;
  lap: number;
  finished: boolean;
  name: string;
};

const drivers: Driver[] = [
  { id: 'bog', name: 'Болотник', color: 0x2fd46c, accent: 0x153b2a, price: 0, owned: true },
  { id: 'bone', name: 'Костяной', color: 0xf4f0dc, accent: 0x24213f, price: 80, owned: false },
  { id: 'slime', name: 'Слайм', color: 0x55f0cf, accent: 0x0a8a75, price: 120, owned: false },
  { id: 'ember', name: 'Огонек', color: 0xff7442, accent: 0x5d1d18, price: 160, owned: false },
  { id: 'warden', name: 'Глубинник', color: 0x123b46, accent: 0x25e6d6, price: 260, owned: false },
  { id: 'magma', name: 'Магмовый', color: 0x2b1613, accent: 0xff8a1c, price: 320, owned: false },
  { id: 'snow', name: 'Снежник', color: 0xe7fbff, accent: 0x3bb8ff, price: 380, owned: false },
  { id: 'mush', name: 'Грибник', color: 0xff4f4f, accent: 0xffffff, price: 440, owned: false },
  { id: 'ender', name: 'Краевик', color: 0x191225, accent: 0xb455ff, price: 520, owned: false },
  { id: 'bee', name: 'Пчелоблок', color: 0xffd23c, accent: 0x1f1d2f, price: 600, owned: false },
  { id: 'pirate', name: 'Пират Кубов', color: 0x78513a, accent: 0xffd166, price: 720, owned: false },
  { id: 'frog', name: 'Прудик', color: 0x63d96b, accent: 0x123d2a, price: 860, owned: false },
  { id: 'phantom', name: 'Фантом', color: 0x6b78ff, accent: 0x10152c, price: 1040, owned: false },
  { id: 'diamond', name: 'Алмазный', color: 0x64f3ff, accent: 0xffffff, price: 1300, owned: false },
];

const kartSkins: KartSkin[] = [
  { id: 'sun', name: 'Солнечный', body: 0xffb000, trim: 0xff4b22, price: 0, owned: true },
  { id: 'forest', name: 'Лесной', body: 0x33c970, trim: 0xf4d23d, price: 90, owned: false },
  { id: 'ice', name: 'Ледяной', body: 0x73d7ff, trim: 0xffffff, price: 130, owned: false },
  { id: 'lava', name: 'Лавовый', body: 0x2b223e, trim: 0xff5d1d, price: 180, owned: false },
  { id: 'thunder', name: 'Гром', body: 0x30343f, trim: 0xffd43b, price: 280, owned: false },
  { id: 'reef', name: 'Риф', body: 0x18b7c9, trim: 0xff71ce, price: 340, owned: false },
  { id: 'moss', name: 'Моховой', body: 0x4d7c32, trim: 0xa3e635, price: 420, owned: false },
  { id: 'royal', name: 'Королевский', body: 0x6d28d9, trim: 0xfacc15, price: 520, owned: false },
  { id: 'ghost', name: 'Призрак', body: 0xdde7ff, trim: 0x8b5cf6, price: 640, owned: false },
  { id: 'copper', name: 'Медный', body: 0xb35c2e, trim: 0x34d399, price: 760, owned: false },
  { id: 'amethyst', name: 'Аметист', body: 0x8b5cf6, trim: 0xf0abfc, price: 920, owned: false },
  { id: 'obsidian', name: 'Обсидиан', body: 0x17111f, trim: 0xf43f5e, price: 1100, owned: false },
  { id: 'quartz', name: 'Кварц', body: 0xf7f1e3, trim: 0x2563eb, price: 1280, owned: false },
  { id: 'dragon', name: 'Дракон', body: 0x111827, trim: 0x22c55e, price: 1600, owned: false },
];

const levels: { id: LevelId; name: string; subtitle: string }[] = [
  { id: 'warmup', name: 'Разминка', subtitle: '+ и - до 20' },
  { id: 'compare', name: 'Сравнение', subtitle: '>, < или =' },
  { id: 'hundred', name: 'Сотня', subtitle: '+ и - до 100' },
  { id: 'table', name: 'Таблица', subtitle: 'умножение и деление' },
  { id: 'missing', name: 'Пропуски', subtitle: 'найди число' },
  { id: 'negative', name: 'Минусы', subtitle: 'числа ниже нуля' },
  { id: 'squares', name: 'Квадраты', subtitle: 'степени и корни' },
  { id: 'percent', name: 'Проценты', subtitle: '10%, 25%, 50%' },
  { id: 'fraction', name: 'Дроби', subtitle: 'простые части' },
  { id: 'equation', name: 'Уравнения', subtitle: 'найди x' },
  { id: 'mix', name: 'Микс', subtitle: 'все режимы' },
];

const storageKey = 'diamond-kart-save-v1';
const totalLaps = 2;
const trackRadiusX = 18;
const trackRadiusZ = 9.8;
const laneSpacing = 0.45;
const uiRootElement = document.querySelector<HTMLDivElement>('#ui-root');
const canvasElement = document.querySelector<HTMLCanvasElement>('#game-canvas');

if (!uiRootElement || !canvasElement) {
  throw new Error('Game root was not found');
}

const uiRoot = uiRootElement;
const canvas = canvasElement;

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x2aa8ff);
scene.fog = new THREE.Fog(0x88d8ff, 42, 82);

const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, preserveDrawingBuffer: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.18;

const camera = new THREE.PerspectiveCamera(42, 16 / 9, 0.1, 120);
const clock = new THREE.Clock();
const raceGroup = new THREE.Group();
scene.add(raceGroup);

let screen: Screen = 'menu';
let selectedLevel: LevelId = 'warmup';
let save = loadSave();
let racers: Racer[] = [];
let currentQuestion = makeQuestion(selectedLevel);
let questionStartedAt = performance.now();
let raceStartedAt = 0;
let raceFinishedAt = 0;
let answered = 0;
let correct = 0;
let streak = 0;
let countdown = 3;
let raceActive = false;
let resultDiamonds = 0;
let cameraMode: CameraMode = 'follow';

function loadSave(): SaveData {
  const fallback: SaveData = {
    diamonds: 120,
    ownedDrivers: ['bog'],
    ownedKarts: ['sun'],
    selectedDriver: 'bog',
    selectedKart: 'sun',
  };

  try {
    const raw = localStorage.getItem(storageKey);
    if (!raw) return fallback;
    return { ...fallback, ...JSON.parse(raw) } as SaveData;
  } catch {
    return fallback;
  }
}

function persistSave() {
  localStorage.setItem(storageKey, JSON.stringify(save));
}

function rand(min: number, max: number) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function shuffle<T>(items: T[]) {
  return [...items].sort(() => Math.random() - 0.5);
}

function uniqueAnswers(correctAnswer: number, spread: number, count = 4) {
  const values = new Set<number>([correctAnswer]);
  while (values.size < count) {
    const candidate = Math.max(0, correctAnswer + rand(-spread, spread));
    values.add(candidate);
  }
  return shuffle([...values]).map(String);
}

function optionAnswers(correctAnswer: string, options: string[], count = 4) {
  const values = new Set<string>([correctAnswer]);
  shuffle(options).forEach((option) => {
    if (values.size < count) values.add(option);
  });
  return shuffle([...values]);
}

function makeQuestion(level: LevelId): Question {
  const picked = level === 'mix' ? levels[rand(0, levels.length - 2)].id : level;
  if (picked === 'compare') {
    const a = rand(2, 99);
    const b = Math.random() > 0.25 ? rand(2, 99) : a;
    const correctSign = a > b ? '>' : a < b ? '<' : '=';
    return { title: 'Выбери знак', text: `${a} __ ${b}`, answers: ['>', '<', '='], correct: correctSign };
  }

  if (picked === 'table') {
    const a = rand(2, 9);
    const b = rand(2, 9);
    const product = a * b;
    const divide = Math.random() > 0.5;
    return divide
      ? { title: 'Реши пример', text: `${product} : ${a}`, answers: uniqueAnswers(b, 4), correct: String(b) }
      : { title: 'Реши пример', text: `${a} x ${b}`, answers: uniqueAnswers(product, 12), correct: String(product) };
  }

  if (picked === 'missing') {
    const a = rand(2, 12);
    const x = rand(2, 12);
    const sum = a + x;
    return {
      title: 'Заполни пропуск',
      text: `${a} + __ = ${sum}`,
      answers: uniqueAnswers(x, 5),
      correct: String(x),
    };
  }

  if (picked === 'negative') {
    const a = rand(-20, 20);
    const b = rand(-15, 15);
    const plus = Math.random() > 0.5;
    const value = plus ? a + b : a - b;
    return {
      title: 'Считай с минусами',
      text: `${a} ${plus ? '+' : '-'} ${b}`,
      answers: uniqueAnswers(value, 9),
      correct: String(value),
    };
  }

  if (picked === 'squares') {
    const n = rand(2, 12);
    if (Math.random() > 0.5) {
      return {
        title: 'Квадрат числа',
        text: `${n}²`,
        answers: uniqueAnswers(n * n, 18),
        correct: String(n * n),
      };
    }
    const square = n * n;
    return {
      title: 'Найди корень',
      text: `√${square}`,
      answers: uniqueAnswers(n, 5),
      correct: String(n),
    };
  }

  if (picked === 'percent') {
    const percents = [10, 20, 25, 50, 75];
    const bases = [40, 60, 80, 100, 120, 160, 200];
    const percent = percents[rand(0, percents.length - 1)];
    const base = bases[rand(0, bases.length - 1)];
    const value = (base * percent) / 100;
    return {
      title: 'Найди процент',
      text: `${percent}% от ${base}`,
      answers: uniqueAnswers(value, 18),
      correct: String(value),
    };
  }

  if (picked === 'fraction') {
    const tasks = [
      { text: '1/2 от 12', correct: '6', options: ['4', '5', '6', '8'] },
      { text: '1/3 от 18', correct: '6', options: ['3', '6', '9', '12'] },
      { text: '1/4 от 20', correct: '5', options: ['4', '5', '8', '10'] },
      { text: '2/3 от 15', correct: '10', options: ['5', '8', '10', '12'] },
      { text: '3/4 от 24', correct: '18', options: ['12', '16', '18', '20'] },
      { text: '1/5 от 35', correct: '7', options: ['5', '6', '7', '9'] },
    ];
    const task = tasks[rand(0, tasks.length - 1)];
    return {
      title: 'Дроби',
      text: task.text,
      answers: optionAnswers(task.correct, task.options),
      correct: task.correct,
    };
  }

  if (picked === 'equation') {
    const x = rand(2, 18);
    const a = rand(3, 20);
    const multiply = Math.random() > 0.55;
    if (multiply) {
      const m = rand(2, 9);
      return {
        title: 'Найди x',
        text: `${m} x x = ${m * x}`,
        answers: uniqueAnswers(x, 6),
        correct: String(x),
      };
    }
    return {
      title: 'Найди x',
      text: `x + ${a} = ${x + a}`,
      answers: uniqueAnswers(x, 7),
      correct: String(x),
    };
  }

  const max = picked === 'hundred' ? 100 : 20;
  const a = rand(1, max);
  const b = rand(1, max);
  const plus = Math.random() > 0.45;
  const left = plus ? a : Math.max(a, b);
  const right = plus ? b : Math.min(a, b);
  const value = plus ? left + right : left - right;
  return {
    title: 'Реши пример',
    text: `${left} ${plus ? '+' : '-'} ${right}`,
    answers: uniqueAnswers(value, picked === 'hundred' ? 18 : 7),
    correct: String(value),
  };
}

function makeBox(w: number, h: number, d: number, color: number, x = 0, y = 0, z = 0) {
  const mesh = new THREE.Mesh(
    new THREE.BoxGeometry(w, h, d),
    new THREE.MeshStandardMaterial({ color, roughness: 0.72, metalness: 0.02 }),
  );
  mesh.position.set(x, y, z);
  mesh.castShadow = true;
  mesh.receiveShadow = true;
  return mesh;
}

function makeMaterial(color: number, roughness = 0.72, texture = false) {
  const material = new THREE.MeshStandardMaterial({ color, roughness, metalness: 0.02 });
  if (texture) {
    material.map = makePixelTexture(color);
  }
  return material;
}

function makePixelTexture(baseColor: number) {
  const canvasTexture = document.createElement('canvas');
  canvasTexture.width = 64;
  canvasTexture.height = 64;
  const context = canvasTexture.getContext('2d');
  const base = new THREE.Color(baseColor);
  if (!context) return null;

  for (let y = 0; y < 8; y++) {
    for (let x = 0; x < 8; x++) {
      const shade = 0.82 + Math.random() * 0.32;
      const color = base.clone().multiplyScalar(shade);
      context.fillStyle = `rgb(${Math.round(color.r * 255)}, ${Math.round(color.g * 255)}, ${Math.round(color.b * 255)})`;
      context.fillRect(x * 8, y * 8, 8, 8);
    }
  }

  const texture = new THREE.CanvasTexture(canvasTexture);
  texture.magFilter = THREE.NearestFilter;
  texture.minFilter = THREE.NearestFilter;
  texture.wrapS = THREE.RepeatWrapping;
  texture.wrapT = THREE.RepeatWrapping;
  return texture;
}

function createKart(driver: Driver, skin: KartSkin) {
  const group = new THREE.Group();
  const variant = skin.id;
  const buggySkins = ['forest', 'moss', 'copper'];
  const formulaSkins = ['lava', 'thunder', 'obsidian', 'dragon'];
  const wideSkins = ['ice', 'reef', 'ghost', 'quartz'];
  const luxurySkins = ['royal', 'amethyst'];
  const isBuggy = buggySkins.includes(variant);
  const isFormula = formulaSkins.includes(variant);
  const isWide = wideSkins.includes(variant);
  const isLuxury = luxurySkins.includes(variant);
  const bodyWidth = isWide ? 1.72 : isFormula ? 1.34 : isLuxury ? 1.62 : 1.5;
  const bodyLength = isFormula ? 2.28 : isBuggy ? 2.08 : isLuxury ? 2.05 : 1.95;
  const body = makeBox(bodyWidth, 0.34, bodyLength, skin.body, 0, 0.38, 0);
  const cockpit = makeBox(0.78, 0.22, 0.72, 0x1f1d2f, 0, 0.67, 0.28);
  const nose = makeBox(isFormula ? 0.5 : 0.78, 0.25, isFormula ? 1.1 : 0.88, skin.trim, 0, 0.52, -0.72);
  const noseTip = makeBox(isFormula ? 0.28 : 0.46, 0.2, 0.34, skin.body, 0, 0.47, -1.16);
  const stripe = makeBox(isFormula ? 0.16 : 0.2, 0.04, bodyLength - 0.25, 0xffffff, 0, 0.72, -0.16);
  const bumper = makeBox(1.62, 0.18, 0.18, 0x191827, 0, 0.28, -1.15);
  const rearBumper = makeBox(1.52, 0.2, 0.18, 0x191827, 0, 0.31, 1.02);
  const leftPod = makeBox(0.26, 0.26, 1.1, skin.trim, -0.82, 0.42, -0.18);
  const rightPod = makeBox(0.26, 0.26, 1.1, skin.trim, 0.82, 0.42, -0.18);
  const spoiler = makeBox(isBuggy ? 1.15 : 1.45, isBuggy ? 0.28 : 0.18, 0.22, skin.trim, 0, 1.03, 0.98);
  const spoilerLeft = makeBox(0.18, 0.56, 0.16, 0x292638, -0.55, 0.78, 0.94);
  const spoilerRight = makeBox(0.18, 0.56, 0.16, 0x292638, 0.55, 0.78, 0.94);
  const headlightA = makeBox(0.22, 0.14, 0.06, 0xfff5b8, -0.34, 0.49, -1.27);
  const headlightB = makeBox(0.22, 0.14, 0.06, 0xfff5b8, 0.34, 0.49, -1.27);
  const exhaustA = makeBox(0.18, 0.18, 0.45, 0x52525b, -0.46, 0.42, 1.2);
  const exhaustB = makeBox(0.18, 0.18, 0.45, 0x52525b, 0.46, 0.42, 1.2);
  const pipeA = makeBox(0.12, 0.12, 0.3, 0xa1a1aa, -0.46, 0.41, 1.53);
  const pipeB = makeBox(0.12, 0.12, 0.3, 0xa1a1aa, 0.46, 0.41, 1.53);
  const sidePlateA = makeBox(0.14, 0.34, 0.68, 0xffffff, -0.96, 0.5, -0.58);
  const sidePlateB = makeBox(0.14, 0.34, 0.68, 0xffffff, 0.96, 0.5, -0.58);
  group.add(
    body,
    cockpit,
    nose,
    noseTip,
    stripe,
    bumper,
    rearBumper,
    leftPod,
    rightPod,
    spoiler,
    spoilerLeft,
    spoilerRight,
    headlightA,
    headlightB,
    exhaustA,
    exhaustB,
    pipeA,
    pipeB,
    sidePlateA,
    sidePlateB,
  );

  if (isBuggy) {
    group.add(makeBox(1.25, 0.12, 0.12, 0x1f1d2f, 0, 1.28, 0.18));
    group.add(makeBox(0.12, 0.7, 0.12, 0x1f1d2f, -0.52, 0.98, 0.18));
    group.add(makeBox(0.12, 0.7, 0.12, 0x1f1d2f, 0.52, 0.98, 0.18));
  }

  if (isWide) {
    group.add(makeBox(1.85, 0.12, 0.34, 0xffffff, 0, 0.5, -1.02));
    group.add(makeBox(1.9, 0.1, 0.28, skin.trim, 0, 0.62, 1.04));
  }

  if (isLuxury) {
    group.add(makeBox(0.26, 0.2, 0.26, 0xfff3b0, -0.52, 0.82, -0.42));
    group.add(makeBox(0.26, 0.2, 0.26, 0xfff3b0, 0.52, 0.82, -0.42));
    group.add(makeBox(1.1, 0.08, 0.18, skin.trim, 0, 1.22, 0.6));
  }

  const wheelMaterial = new THREE.MeshStandardMaterial({ color: 0x252035, roughness: 0.65 });
  const hubMaterial = new THREE.MeshStandardMaterial({ color: skin.trim, roughness: 0.5 });
  for (const x of [-0.88, 0.88]) {
    for (const z of [-0.68, 0.68]) {
      const wheel = new THREE.Mesh(new THREE.CylinderGeometry(0.31, 0.31, 0.32, 12), wheelMaterial);
      wheel.rotation.z = Math.PI / 2;
      wheel.position.set(x, 0.25, z);
      wheel.castShadow = true;
      group.add(wheel);

      const hub = new THREE.Mesh(new THREE.CylinderGeometry(0.14, 0.14, 0.34, 10), hubMaterial);
      hub.rotation.z = Math.PI / 2;
      hub.position.copy(wheel.position);
      hub.castShadow = true;
      group.add(hub);
    }
  }

  const torso = makeBox(0.44, 0.42, 0.34, driver.color, 0, 0.98, 0.14);
  const head = makeBox(0.58, 0.52, 0.58, driver.color, 0, 1.45, -0.02);
  const face = makeBox(0.42, 0.18, 0.04, 0xfff1d8, 0, 1.43, -0.32);
  const eyeA = makeBox(0.08, 0.13, 0.05, driver.accent, -0.12, 1.45, -0.35);
  const eyeB = makeBox(0.08, 0.13, 0.05, driver.accent, 0.12, 1.45, -0.35);
  const handA = makeBox(0.16, 0.18, 0.16, driver.color, -0.32, 1.02, -0.22);
  const handB = makeBox(0.16, 0.18, 0.16, driver.color, 0.32, 1.02, -0.22);
  const wheel = makeBox(0.58, 0.08, 0.08, 0x191827, 0, 0.97, -0.36);
  group.add(torso, head, face, eyeA, eyeB, handA, handB, wheel);

  if (driver.id === 'bog') {
    group.add(makeBox(0.12, 0.18, 0.08, 0x101820, -0.12, 1.45, -0.38));
    group.add(makeBox(0.12, 0.18, 0.08, 0x101820, 0.12, 1.45, -0.38));
    group.add(makeBox(0.12, 0.24, 0.08, 0x101820, 0, 1.3, -0.38));
  }
  if (driver.id === 'bone') {
    group.add(makeBox(0.68, 0.08, 0.08, 0x2a2940, 0, 1.7, -0.02));
    group.add(makeBox(0.08, 0.64, 0.08, 0x2a2940, -0.24, 1.45, -0.02));
    group.add(makeBox(0.08, 0.64, 0.08, 0x2a2940, 0.24, 1.45, -0.02));
  }
  if (driver.id === 'slime') {
    group.add(makeBox(0.7, 0.14, 0.7, 0x9fffe9, 0, 1.77, -0.02));
    group.add(makeBox(0.78, 0.08, 0.78, 0x1fc6a9, 0, 1.15, -0.02));
  }
  if (driver.id === 'ember') {
    group.add(makeBox(0.2, 0.34, 0.2, 0xffd43b, -0.18, 1.78, -0.05));
    group.add(makeBox(0.2, 0.42, 0.2, 0xff4b22, 0.16, 1.84, -0.05));
    group.add(makeBox(0.12, 0.2, 0.12, 0x2b100b, 0, 1.66, -0.38));
  }
  if (driver.id === 'warden') {
    group.add(makeBox(0.16, 0.16, 0.08, driver.accent, -0.16, 1.5, -0.38));
    group.add(makeBox(0.16, 0.16, 0.08, driver.accent, 0.16, 1.5, -0.38));
    group.add(makeBox(0.5, 0.1, 0.08, driver.accent, 0, 1.3, -0.38));
  }
  if (driver.id === 'magma') {
    group.add(makeBox(0.6, 0.08, 0.62, 0xff6b1a, 0, 1.64, -0.02));
    group.add(makeBox(0.12, 0.1, 0.08, 0xffe04d, -0.12, 1.45, -0.38));
    group.add(makeBox(0.12, 0.1, 0.08, 0xffe04d, 0.12, 1.45, -0.38));
  }
  if (driver.id === 'snow') {
    group.add(makeBox(0.66, 0.18, 0.66, 0x69d2ff, 0, 1.74, -0.02));
    group.add(makeBox(0.72, 0.1, 0.72, 0xffffff, 0, 1.2, -0.02));
  }
  if (driver.id === 'mush') {
    group.add(makeBox(0.82, 0.22, 0.82, 0xff3131, 0, 1.76, -0.02));
    group.add(makeBox(0.16, 0.08, 0.08, 0xffffff, -0.22, 1.78, -0.36));
    group.add(makeBox(0.16, 0.08, 0.08, 0xffffff, 0.22, 1.78, -0.36));
  }
  if (driver.id === 'ender') {
    group.add(makeBox(0.62, 0.12, 0.62, 0x0c0714, 0, 1.72, -0.02));
    group.add(makeBox(0.14, 0.08, 0.08, driver.accent, -0.12, 1.48, -0.38));
    group.add(makeBox(0.14, 0.08, 0.08, driver.accent, 0.12, 1.48, -0.38));
  }
  if (driver.id === 'bee') {
    group.add(makeBox(0.7, 0.08, 0.12, 0x191827, 0, 1.57, -0.35));
    group.add(makeBox(0.72, 0.18, 0.08, 0xe8f7ff, -0.4, 1.28, 0.08));
    group.add(makeBox(0.72, 0.18, 0.08, 0xe8f7ff, 0.4, 1.28, 0.08));
  }
  if (driver.id === 'pirate') {
    group.add(makeBox(0.7, 0.12, 0.68, 0x291610, 0, 1.75, -0.02));
    group.add(makeBox(0.16, 0.1, 0.08, 0x111111, -0.12, 1.46, -0.38));
    group.add(makeBox(0.34, 0.04, 0.08, 0xfff1d8, 0.13, 1.46, -0.38));
  }
  if (driver.id === 'frog') {
    group.add(makeBox(0.18, 0.18, 0.18, driver.color, -0.24, 1.78, -0.18));
    group.add(makeBox(0.18, 0.18, 0.18, driver.color, 0.24, 1.78, -0.18));
    group.add(makeBox(0.1, 0.08, 0.08, driver.accent, -0.24, 1.8, -0.32));
    group.add(makeBox(0.1, 0.08, 0.08, driver.accent, 0.24, 1.8, -0.32));
  }
  if (driver.id === 'phantom') {
    group.add(makeBox(0.88, 0.08, 0.16, 0xa5b4fc, 0, 1.72, 0.08));
    group.add(makeBox(0.16, 0.1, 0.08, 0xffffff, -0.12, 1.46, -0.38));
    group.add(makeBox(0.16, 0.1, 0.08, 0xffffff, 0.12, 1.46, -0.38));
  }
  if (driver.id === 'diamond') {
    group.add(makeBox(0.72, 0.14, 0.72, 0xcffafe, 0, 1.72, -0.02));
    group.add(makeBox(0.16, 0.16, 0.08, 0xffffff, -0.14, 1.48, -0.38));
    group.add(makeBox(0.16, 0.16, 0.08, 0xffffff, 0.14, 1.48, -0.38));
  }

  const flagPole = makeBox(0.06, 0.92, 0.06, 0x27243f, 0.5, 1.12, 0.86);
  const flag = makeBox(0.42, 0.28, 0.05, skin.trim, 0.7, 1.42, 0.86);
  group.add(flagPole, flag);
  group.scale.setScalar(0.86);
  return group;
}

function createTrack(showShowcase = false) {
  raceGroup.clear();

  const hemi = new THREE.HemisphereLight(0xffffff, 0x58714b, 1.55);
  raceGroup.add(hemi);
  const sun = new THREE.DirectionalLight(0xffffff, 2.4);
  sun.position.set(-12, 22, 10);
  sun.castShadow = true;
  sun.shadow.mapSize.set(2048, 2048);
  sun.shadow.camera.left = -35;
  sun.shadow.camera.right = 35;
  sun.shadow.camera.top = 25;
  sun.shadow.camera.bottom = -25;
  raceGroup.add(sun);

  const ground = new THREE.Mesh(
    new THREE.PlaneGeometry(72, 48),
    makeMaterial(0x5fd243, 0.86, true),
  );
  ground.rotation.x = -Math.PI / 2;
  ground.receiveShadow = true;
  raceGroup.add(ground);

  const roadMaterial = makeMaterial(0x3e4246, 0.9, true);
  const innerMaterial = makeMaterial(0x65d84b, 0.86, true);
  const curbRed = makeMaterial(0xdf3f37, 0.68, true);
  const curbWhite = makeMaterial(0xf4f1e9, 0.68, true);
  const lineMaterial = makeMaterial(0xf3f4ee, 0.75, false);

  for (let i = 0; i < 192; i++) {
    const t = (i / 192) * Math.PI * 2;
    const pos = pointOnTrack(t, 0);
    const tangent = tangentOnTrack(t);
    const rotation = Math.atan2(tangent.x, tangent.z);
    const road = new THREE.Mesh(new THREE.BoxGeometry(2.55, 0.16, 2.55), roadMaterial);
    road.position.set(pos.x, 0.06, pos.z);
    road.rotation.y = rotation;
    road.receiveShadow = true;
    raceGroup.add(road);

    if (i % 3 === 0) {
      for (const side of [-4.2, 4.2]) {
        const curbPos = pointOnTrack(t, side);
        const curb = new THREE.Mesh(new THREE.BoxGeometry(0.76, 0.34, 1.38), i % 6 === 0 ? curbRed : curbWhite);
        curb.position.set(curbPos.x, 0.22, curbPos.z);
        curb.rotation.y = rotation;
        curb.castShadow = true;
        curb.receiveShadow = true;
        raceGroup.add(curb);
      }
    }

    if (i % 12 === 0) {
      const linePos = pointOnTrack(t, 0);
      const line = new THREE.Mesh(new THREE.BoxGeometry(0.18, 0.18, 0.9), lineMaterial);
      line.position.set(linePos.x, 0.19, linePos.z);
      line.rotation.y = rotation;
      raceGroup.add(line);
    }
  }

  const island = new THREE.Mesh(new THREE.BoxGeometry(19, 0.22, 8.8), innerMaterial);
  island.position.y = 0.08;
  island.receiveShadow = true;
  raceGroup.add(island);

  addLake();
  addMountains();
  addBiomeBlocks();
  addTrackDetails();
  addClouds();
  addFinishLine();
  if (showShowcase) addShowcaseKarts();
}

function addShowcaseKarts() {
  const showcase = [
    { x: -5.2, z: -5.1, rotation: -0.16, driver: drivers[2], kart: kartSkins[2] },
    { x: 0, z: -5.35, rotation: -0.04, driver: drivers[1], kart: kartSkins[3] },
    { x: 5.2, z: -5.05, rotation: 0.12, driver: drivers[3], kart: kartSkins[1] },
  ];

  showcase.forEach((item) => {
    const kart = createKart(item.driver, item.kart);
    kart.position.set(item.x, 0.08, item.z);
    kart.rotation.y = item.rotation;
    kart.scale.setScalar(1.15);
    raceGroup.add(kart);
  });
}

function addBiomeBlocks() {
  const decorations = [
    { color: 0x25a75b, x: -20, z: -10, type: 'trees' },
    { color: 0xf2c66d, x: 19, z: -10, type: 'sand' },
    { color: 0xdff7ff, x: 20, z: 10, type: 'snow' },
    { color: 0x6d7188, x: -18, z: 10, type: 'rocks' },
  ];

  decorations.forEach((biome) => {
    const base = makeBox(11, 0.18, 8, biome.color, biome.x, 0.09, biome.z);
    raceGroup.add(base);
    for (let i = 0; i < 14; i++) {
      const x = biome.x + rand(-4, 4);
      const z = biome.z + rand(-3, 3);
      if (biome.type === 'trees') {
        raceGroup.add(makeBox(0.5, 1.8, 0.5, 0x815332, x, 0.95, z));
        raceGroup.add(makeBox(1.65, 1.1, 1.65, 0x1fb756, x, 2.2, z));
        raceGroup.add(makeBox(1.15, 0.85, 1.15, 0x58d53e, x + 0.25, 2.85, z - 0.15));
      } else if (biome.type === 'rocks') {
        raceGroup.add(makeBox(rand(8, 18) / 10, rand(5, 18) / 10, rand(8, 18) / 10, 0x76798d, x, 0.5, z));
      } else if (biome.type === 'snow') {
        raceGroup.add(makeBox(1, 1.2, 1, 0xbcecff, x, 0.65, z));
        raceGroup.add(makeBox(0.7, 0.7, 0.7, 0xffffff, x, 1.55, z));
      } else {
        raceGroup.add(makeBox(0.8, 1.4, 0.8, 0xd9a54f, x, 0.75, z));
        raceGroup.add(makeBox(1.5, 0.42, 1.5, 0x53c567, x, 1.55, z));
      }

      if (i % 3 === 0) {
        raceGroup.add(makeBox(0.16, 0.42, 0.16, 0x45c65b, x + 0.8, 0.3, z + 0.6));
        raceGroup.add(makeBox(0.18, 0.18, 0.18, i % 2 ? 0xffffff : 0xff77b7, x + 0.8, 0.62, z + 0.6));
      }
    }
  });

  for (let i = 0; i < 18; i++) {
    const x = rand(-31, 31);
    const z = rand(-20, 20);
    if (Math.abs(x) < 19 && Math.abs(z) < 12) continue;
    const trunk = makeBox(0.58, 2.0, 0.58, 0x8a552e, x, 1, z);
    const leavesA = makeBox(1.85, 1.15, 1.85, 0x2fbf45, x, 2.35, z);
    const leavesB = makeBox(1.22, 0.85, 1.22, 0x7ad83d, x + 0.18, 3.05, z - 0.08);
    raceGroup.add(trunk, leavesA, leavesB);
  }
}

function addLake() {
  const waterMaterial = new THREE.MeshStandardMaterial({
    color: 0x22b7ff,
    roughness: 0.18,
    metalness: 0,
    transparent: true,
    opacity: 0.82,
  });
  const lake = new THREE.Mesh(new THREE.BoxGeometry(8.8, 0.08, 3.9), waterMaterial);
  lake.position.set(0.3, 0.25, 0.15);
  lake.receiveShadow = true;
  raceGroup.add(lake);

  const sand = makeMaterial(0xf0d36f, 0.82, true);
  const shorePieces = [
    [-4.8, 0, 1.2, 4.2],
    [4.8, 0, 1.2, 4.2],
    [0, -2.35, 8.8, 0.9],
    [0, 2.35, 8.8, 0.9],
  ];
  shorePieces.forEach(([x, z, w, d]) => {
    const shore = new THREE.Mesh(new THREE.BoxGeometry(w, 0.12, d), sand);
    shore.position.set(x, 0.24, z);
    shore.receiveShadow = true;
    raceGroup.add(shore);
  });

  for (let i = 0; i < 9; i++) {
    const fish = new THREE.Group();
    const fishColor = i % 3 === 0 ? 0xff8a2a : i % 3 === 1 ? 0xff4fb8 : 0xfff04a;
    fish.add(makeBox(0.34, 0.14, 0.18, fishColor, 0, 0, 0));
    fish.add(makeBox(0.14, 0.12, 0.12, 0xffffff, -0.23, 0, 0));
    fish.add(makeBox(0.08, 0.08, 0.2, 0x1f1d2f, 0.21, 0, 0));
    fish.position.set(rand(-34, 34) / 10, 0.36, rand(-14, 14) / 10);
    fish.rotation.y = Math.random() * Math.PI * 2;
    raceGroup.add(fish);
  }

  for (let i = 0; i < 16; i++) {
    raceGroup.add(makeBox(0.1, 0.65, 0.1, 0x2dbd69, rand(-45, 45) / 10, 0.62, rand(-22, 22) / 10));
  }
}

function addMountains() {
  const mountainMaterial = makeMaterial(0x7c8897, 0.86, true);
  const snowMaterial = makeMaterial(0xf4fbff, 0.72, true);
  const mountainData = [
    [-31, -17, 0.2],
    [-24, 18, 0.8],
    [-10, 22, -0.1],
    [8, -21, 0.5],
    [24, 17, 0.9],
    [31, -8, 0.3],
  ];

  mountainData.forEach(([x, z, heightOffset]) => {
    for (let layer = 0; layer < 5; layer++) {
      const size = 5.8 - layer * 0.9;
      const block = new THREE.Mesh(new THREE.BoxGeometry(size, 0.9, size), mountainMaterial);
      block.position.set(x, 0.52 + layer * 0.86 + heightOffset, z);
      block.castShadow = true;
      block.receiveShadow = true;
      raceGroup.add(block);
    }
    const cap = new THREE.Mesh(new THREE.BoxGeometry(2.4, 0.65, 2.4), snowMaterial);
    cap.position.set(x, 4.72 + heightOffset, z);
    cap.castShadow = true;
    raceGroup.add(cap);
  });
}

function addTrackDetails() {
  const markerTs = [0.1, 0.34, 0.62, 0.86, 1.18, 1.45, 1.82, 2.12, 2.54, 2.86, 3.22, 3.58, 4.02, 4.42, 4.84, 5.32, 5.78];
  markerTs.forEach((t, index) => {
    const side = index % 2 === 0 ? 5.8 : -5.8;
    const pos = pointOnTrack(t, side);
    const tangent = tangentOnTrack(t);
    const rotation = Math.atan2(tangent.x, tangent.z);
    const post = makeBox(0.16, 0.9, 0.16, 0x3b3128, pos.x, 0.55, pos.z);
    const sign = makeBox(0.9, 0.46, 0.08, index % 3 === 0 ? 0xffd33d : 0x2dd4ff, pos.x, 1.15, pos.z);
    const arrow = makeBox(0.46, 0.16, 0.1, 0x191827, pos.x, 1.15, pos.z);
    post.rotation.y = rotation;
    sign.rotation.y = rotation;
    arrow.rotation.y = rotation + (side > 0 ? -0.35 : 0.35);
    raceGroup.add(post, sign, arrow);
  });

  for (let i = 0; i < 24; i++) {
    const t = (i / 24) * Math.PI * 2 + 0.08;
    const side = i % 2 === 0 ? 6.8 : -6.8;
    const pos = pointOnTrack(t, side);
    const crystal = makeBox(0.22, 0.38, 0.22, 0x34d8ff, pos.x, 0.38, pos.z);
    crystal.rotation.y = t;
    crystal.rotation.z = 0.25;
    raceGroup.add(crystal);
  }

  const pads = [
    { t: 0.52, side: 0, color: 0x29e65f },
    { t: 2.42, side: 0, color: 0x29e65f },
    { t: 4.45, side: 0, color: 0x29e65f },
  ];
  pads.forEach((pad) => {
    const pos = pointOnTrack(pad.t, pad.side);
    const tangent = tangentOnTrack(pad.t);
    const boostPad = makeBox(1.25, 0.05, 0.72, pad.color, pos.x, 0.26, pos.z);
    boostPad.rotation.y = Math.atan2(tangent.x, tangent.z);
    raceGroup.add(boostPad);
  });
}

function addClouds() {
  const cloudMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.55, transparent: true, opacity: 0.86 });
  const cloudPositions = [
    [-18, 12, -22],
    [7, 15, -24],
    [24, 13, -14],
    [-8, 16, 19],
  ];
  cloudPositions.forEach(([x, y, z]) => {
    const cloud = new THREE.Group();
    [
      [-1.1, 0, 0, 1.8, 0.5, 0.8],
      [0, 0.15, 0, 1.4, 0.7, 1],
      [1.1, 0, 0.1, 1.7, 0.5, 0.75],
      [0.35, -0.12, -0.65, 1.9, 0.42, 0.55],
    ].forEach(([ox, oy, oz, w, h, d]) => {
      const block = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), cloudMaterial);
      block.position.set(ox, oy, oz);
      cloud.add(block);
    });
    cloud.position.set(x, y, z);
    raceGroup.add(cloud);
  });
}

function addFinishLine() {
  const finishTangent = tangentOnTrack(-Math.PI / 2);
  const finishRotation = Math.atan2(finishTangent.x, finishTangent.z);
  const finish = new THREE.Group();
  for (let x = -3; x <= 3; x++) {
    for (let z = -1; z <= 1; z++) {
      const square = makeBox(0.52, 0.06, 0.52, (x + z) % 2 === 0 ? 0xffffff : 0x1f1f25, x * 0.52, 0.25, z * 0.52);
      finish.add(square);
    }
  }
  finish.add(makeBox(0.28, 3.6, 0.28, 0x3b3d42, -2.6, 1.9, 0));
  finish.add(makeBox(0.28, 3.6, 0.28, 0x3b3d42, 2.6, 1.9, 0));
  for (let i = 0; i < 10; i++) {
    const color = i === 0 || i === 9 ? 0xe23b35 : i % 2 === 0 ? 0xffffff : 0x15151b;
    finish.add(makeBox(0.52, 0.42, 0.34, color, -2.34 + i * 0.52, 3.55, 0));
  }
  const finishPos = pointOnTrack(-Math.PI / 2, 0);
  finish.position.set(finishPos.x, 0, finishPos.z);
  finish.rotation.y = finishRotation;
  raceGroup.add(finish);
}

function pointOnTrack(t: number, lane: number) {
  const center = centerPointOnTrack(t);
  const tangent = tangentOnTrack(t);
  const normal = new THREE.Vector3(tangent.z, 0, -tangent.x).normalize();
  return center.add(normal.multiplyScalar(lane * laneSpacing));
}

function tangentOnTrack(t: number) {
  const before = centerPointOnTrack(t - 0.01);
  const after = centerPointOnTrack(t + 0.01);
  return after.sub(before).normalize();
}

function centerPointOnTrack(t: number) {
  const radiusWaveX = 1 + 0.13 * Math.sin(t * 3 + 0.4) + 0.07 * Math.sin(t * 5 - 0.9);
  const radiusWaveZ = 1 + 0.15 * Math.cos(t * 4 - 0.2) + 0.06 * Math.sin(t * 6 + 1.4);
  const chicaneX = 1.9 * Math.sin(t * 2.0 + 0.65);
  const chicaneZ = 1.25 * Math.sin(t * 3.0 - 0.35);
  return new THREE.Vector3(
    Math.cos(t) * trackRadiusX * radiusWaveX + chicaneX,
    0,
    Math.sin(t) * trackRadiusZ * radiusWaveZ + chicaneZ,
  );
}

function setupRace() {
  createTrack();
  const selectedDriver = drivers.find((driver) => driver.id === save.selectedDriver) ?? drivers[0];
  const selectedKart = kartSkins.find((skin) => skin.id === save.selectedKart) ?? kartSkins[0];
  const botDrivers = drivers.filter((driver) => driver.id !== selectedDriver.id);
  const botKarts = kartSkins.filter((skin) => skin.id !== selectedKart.id);

  racers = [
    makeRacer('player', 'Ты', selectedDriver, selectedKart, 0, false),
    makeRacer('bot-1', 'Быстрик', botDrivers[0] ?? drivers[1], botKarts[0] ?? kartSkins[1], 1, true, 0.68),
    makeRacer('bot-2', 'Ровный', botDrivers[1] ?? drivers[2], botKarts[1] ?? kartSkins[2], -1, true, 0.82),
    makeRacer('bot-3', 'Тихоня', botDrivers[2] ?? drivers[3], botKarts[2] ?? kartSkins[3], 2, true, 0.95),
  ];

  racers.forEach((racer, index) => {
    racer.progress = -0.02 - index * 0.012;
    raceGroup.add(racer.group);
    placeRacer(racer, 1);
  });

  answered = 0;
  correct = 0;
  streak = 0;
  countdown = 3;
  raceActive = false;
  raceStartedAt = performance.now();
  raceFinishedAt = 0;
  currentQuestion = makeQuestion(selectedLevel);
  questionStartedAt = performance.now();

  setTimeout(() => (countdown = 2), 850);
  setTimeout(() => (countdown = 1), 1700);
  setTimeout(() => {
    countdown = 0;
    raceActive = true;
    raceStartedAt = performance.now();
    render();
  }, 2550);
}

function makeRacer(id: string, name: string, driver: Driver, skin: KartSkin, lane: number, bot = false, skill = 0.8): Racer {
  return {
    id,
    name,
    group: createKart(driver, skin),
    lane,
    progress: 0,
    speed: bot ? 0.016 : 0.012,
    boost: 0,
    botTimer: rand(2400, 5200),
    answerSkill: skill,
    lap: 1,
    finished: false,
  };
}

function answer(value: string) {
  if (screen !== 'race' || !raceActive) return;
  answered += 1;
  const player = racers[0];
  if (value === currentQuestion.correct) {
    correct += 1;
    streak += 1;
    const quickBonus = Math.max(0.65, 1.2 - (performance.now() - questionStartedAt) / 12000);
    player.boost += 0.095 * quickBonus + Math.min(streak, 5) * 0.007;
    burst(player.group.position, 0x2be85f);
  } else {
    streak = 0;
    player.speed = Math.max(0.01, player.speed - 0.004);
    burst(player.group.position, 0x3b3548);
  }
  currentQuestion = makeQuestion(selectedLevel);
  questionStartedAt = performance.now();
  render();
}

function burst(position: THREE.Vector3, color: number) {
  for (let i = 0; i < 12; i++) {
    const particle = makeBox(0.12, 0.12, 0.12, color, position.x + (Math.random() - 0.5), 0.7 + Math.random(), position.z + (Math.random() - 0.5));
    raceGroup.add(particle);
    setTimeout(() => raceGroup.remove(particle), 450 + i * 18);
  }
}

function updateRace(dt: number) {
  if (screen !== 'race') return;
  const elapsed = performance.now() - raceStartedAt;
  racers.forEach((racer, index) => {
    if (racer.finished || !raceActive) return;

    if (index > 0) {
      racer.botTimer -= dt * 1000;
      if (racer.botTimer <= 0) {
        if (Math.random() < racer.answerSkill) {
          racer.boost += 0.055 + Math.random() * 0.045;
          burst(racer.group.position, 0xffd83f);
        }
        racer.botTimer = rand(2600, 6200);
      }
    }

    const baseSpeed = racer.id === 'player' ? 0.011 : 0.013;
    const boostSpeed = racer.boost;
    racer.progress += dt * (baseSpeed + boostSpeed);
    racer.boost = Math.max(0, racer.boost - dt * 0.105);
    racer.speed = baseSpeed + boostSpeed;

    if (racer.progress >= totalLaps) {
      racer.progress = totalLaps;
      racer.finished = true;
      if (racer.id === 'player') finishRace();
    }

    racer.lap = Math.min(totalLaps, Math.floor(racer.progress) + 1);
    placeRacer(racer, 0.5);
    racer.group.rotation.z = Math.sin(elapsed / 140 + index) * 0.035;
  });

  const player = racers[0];
  if (player) {
    updateCamera(player);
  }
}

function updateCamera(player: Racer) {
  const target = player.group.position.clone();
  const forward = getForwardVector(player.group.rotation.y);
  if (cameraMode === 'first') {
    const cameraTarget = target.clone().add(new THREE.Vector3(0, 1.35, 0)).add(forward.clone().multiplyScalar(0.72));
    const lookTarget = cameraTarget.clone().add(forward.clone().multiplyScalar(7.5));
    camera.position.lerp(cameraTarget, 0.28);
    camera.lookAt(lookTarget.x, lookTarget.y + 0.15, lookTarget.z);
    return;
  }

  camera.position.lerp(new THREE.Vector3(target.x - 7.5, 8.2, target.z + 10), 0.07);
  camera.lookAt(target.x + 2.5, 0.8, target.z);
}

function getForwardVector(rotationY: number) {
  return new THREE.Vector3(-Math.sin(rotationY), 0, -Math.cos(rotationY)).normalize();
}

function placeRacer(racer: Racer, lerp = 1) {
  const t = (racer.progress % 1) * Math.PI * 2 - Math.PI / 2;
  const pos = pointOnTrack(t, racer.lane);
  const tangent = tangentOnTrack(t);
  const target = new THREE.Vector3(pos.x, 0.05, pos.z);
  if (lerp >= 1) {
    racer.group.position.copy(target);
  } else {
    racer.group.position.lerp(target, lerp);
  }
  racer.group.rotation.y = Math.atan2(tangent.x, tangent.z) + Math.PI;
}

function finishRace() {
  if (raceFinishedAt) return;
  raceFinishedAt = performance.now();
  raceActive = false;
  const accuracy = answered ? correct / answered : 0;
  const place = getPlayerPlace();
  resultDiamonds = Math.max(12, Math.round(55 * accuracy + 18 * streak + (place === 1 ? 45 : place === 2 ? 25 : 10)));
  save.diamonds += resultDiamonds;
  persistSave();
  setTimeout(() => {
    screen = 'result';
    render();
  }, 900);
}

function getPlayerPlace() {
  const sorted = [...racers].sort((a, b) => b.progress - a.progress);
  return sorted.findIndex((racer) => racer.id === 'player') + 1;
}

function resize() {
  const width = window.innerWidth;
  const height = window.innerHeight;
  renderer.setSize(width, height);
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
}

function gameLoop() {
  const dt = Math.min(clock.getDelta(), 0.05);
  updateRace(dt);
  renderer.render(scene, camera);
  requestAnimationFrame(gameLoop);
}

function go(next: Screen) {
  screen = next;
  if (next === 'menu') {
    raceActive = false;
    createTrack(true);
    camera.position.set(-8, 8.6, 12.5);
    camera.lookAt(1.8, 0.9, -4.8);
  }
  render();
}

function startRace(level: LevelId) {
  selectedLevel = level;
  screen = 'race';
  setupRace();
  render();
}

function buyDriver(id: string) {
  const driver = drivers.find((item) => item.id === id);
  if (!driver) return;
  if (!save.ownedDrivers.includes(id) && save.diamonds >= driver.price) {
    save.diamonds -= driver.price;
    save.ownedDrivers.push(id);
  }
  if (save.ownedDrivers.includes(id)) save.selectedDriver = id;
  persistSave();
  render();
}

function buyKart(id: string) {
  const kart = kartSkins.find((item) => item.id === id);
  if (!kart) return;
  if (!save.ownedKarts.includes(id) && save.diamonds >= kart.price) {
    save.diamonds -= kart.price;
    save.ownedKarts.push(id);
  }
  if (save.ownedKarts.includes(id)) save.selectedKart = id;
  persistSave();
  render();
}

function fullscreen() {
  const app = document.querySelector<HTMLDivElement>('#app');
  if (!document.fullscreenElement) {
    app?.requestFullscreen();
  } else {
    document.exitFullscreen();
  }
}

function toggleCameraMode() {
  cameraMode = cameraMode === 'follow' ? 'first' : 'follow';
  render();
}

function render() {
  const player = racers[0];
  const place = player ? getPlayerPlace() : 1;
  const lap = player ? player.lap : 1;
  const level = levels.find((item) => item.id === selectedLevel) ?? levels[0];

  uiRoot.innerHTML = `
    <div class="topbar">
      <button class="icon-button" data-action="menu">←</button>
      <div class="diamonds">◆ ${save.diamonds}</div>
      <button class="icon-button" data-action="fullscreen">⛶</button>
    </div>
    ${screen === 'menu' ? menuView() : ''}
    ${screen === 'levels' ? levelsView() : ''}
    ${screen === 'garage' ? garageView() : ''}
    ${screen === 'race' ? raceView(level, place, lap) : ''}
    ${screen === 'result' ? resultView(place) : ''}
  `;

  uiRoot.querySelectorAll<HTMLElement>('[data-screen]').forEach((button) => {
    button.addEventListener('click', () => go(button.dataset.screen as Screen));
  });
  uiRoot.querySelectorAll<HTMLElement>('[data-level]').forEach((button) => {
    button.addEventListener('click', () => startRace(button.dataset.level as LevelId));
  });
  uiRoot.querySelectorAll<HTMLElement>('[data-answer]').forEach((button) => {
    button.addEventListener('click', () => answer(button.dataset.answer ?? ''));
  });
  uiRoot.querySelectorAll<HTMLElement>('[data-driver]').forEach((button) => {
    button.addEventListener('click', () => buyDriver(button.dataset.driver ?? ''));
  });
  uiRoot.querySelectorAll<HTMLElement>('[data-kart]').forEach((button) => {
    button.addEventListener('click', () => buyKart(button.dataset.kart ?? ''));
  });
  uiRoot.querySelectorAll<HTMLElement>('[data-action]').forEach((button) => {
    button.addEventListener('click', () => {
      if (button.dataset.action === 'fullscreen') fullscreen();
      if (button.dataset.action === 'menu') go('menu');
      if (button.dataset.action === 'camera') toggleCameraMode();
    });
  });
}

function menuView() {
  return `
    <main class="menu panel">
      <p class="eyebrow">2.5D voxel racing</p>
      <h1>КубоКарт: Математика</h1>
      <p class="lead">Решай примеры, жми на газ бустом и обгоняй трех локальных соперников.</p>
      <div class="menu-actions">
        <button class="primary" data-screen="levels">Играть</button>
        <button data-screen="garage">Гараж</button>
      </div>
    </main>
  `;
}

function levelsView() {
  return `
    <main class="level-panel panel">
      <h2>Выбери уровень</h2>
      <div class="level-grid">
        ${levels
          .map(
            (level) => `
          <button class="level-card" data-level="${level.id}">
            <strong>${level.name}</strong>
            <span>${level.subtitle}</span>
          </button>`,
          )
          .join('')}
      </div>
    </main>
  `;
}

function garageView() {
  return `
    <main class="garage panel">
      <header>
        <h2>Гараж</h2>
        <p>Покупай мобов и картинги за алмазы.</p>
      </header>
      <h3>Водители</h3>
      <div class="shop-grid">
        ${drivers.map((driver) => shopButton(driver.name, driver.id, driver.price, save.ownedDrivers, save.selectedDriver, 'driver')).join('')}
      </div>
      <h3>Картинги</h3>
      <div class="shop-grid">
        ${kartSkins.map((kart) => shopButton(kart.name, kart.id, kart.price, save.ownedKarts, save.selectedKart, 'kart')).join('')}
      </div>
    </main>
  `;
}

function shopButton(name: string, id: string, price: number, owned: string[], selected: string, kind: 'driver' | 'kart') {
  const isOwned = owned.includes(id);
  const isSelected = selected === id;
  return `
    <button class="shop-card ${isSelected ? 'selected' : ''}" data-${kind}="${id}">
      <strong>${name}</strong>
      <span>${isSelected ? 'Выбрано' : isOwned ? 'Выбрать' : `◆ ${price}`}</span>
    </button>
  `;
}

function raceView(level: { name: string }, place: number, lap: number) {
  return `
    <aside class="race-panel">
      <div class="race-stats">
        <span>Место ${place}/4</span>
        <span>Круг ${lap}/${totalLaps}</span>
      </div>
      <button class="camera-toggle" data-action="camera">Камера: ${cameraMode === 'first' ? 'из кабины' : 'сзади'}</button>
      ${countdown > 0 ? `<div class="countdown">${countdown}</div>` : ''}
      <section class="question">
        <p>${level.name}</p>
        <h2>${currentQuestion.title}</h2>
        <div class="example">${currentQuestion.text}</div>
        <div class="answers">
          ${currentQuestion.answers.map((item) => `<button data-answer="${item}">${item}</button>`).join('')}
        </div>
      </section>
      <div class="meter">
        <span>Серия</span>
        <strong>${streak}</strong>
      </div>
    </aside>
  `;
}

function resultView(place: number) {
  const accuracy = answered ? Math.round((correct / answered) * 100) : 0;
  const time = raceFinishedAt ? Math.round((raceFinishedAt - raceStartedAt) / 1000) : 0;
  return `
    <main class="result panel">
      <p class="eyebrow">Финиш</p>
      <h2>${place} место</h2>
      <div class="result-grid">
        <span>Точность</span><strong>${accuracy}%</strong>
        <span>Ответы</span><strong>${correct}/${answered}</strong>
        <span>Время</span><strong>${time} c</strong>
        <span>Награда</span><strong>◆ ${resultDiamonds}</strong>
      </div>
      <div class="menu-actions">
        <button class="primary" data-level="${selectedLevel}">Еще раз</button>
        <button data-screen="garage">Гараж</button>
        <button data-screen="menu">Меню</button>
      </div>
    </main>
  `;
}

window.addEventListener('resize', resize);
resize();
createTrack(true);
camera.position.set(-8, 8.6, 12.5);
camera.lookAt(1.8, 0.9, -4.8);
render();
gameLoop();
