/**
 * levelToScene — превращает LEVELS[] в готовые primitives + blocks для проекта.
 *
 * Используется при save из 2D-редактора. Логика идентична серверному
 * `_p265_redesign.py` (тому что был одноразовым) — теперь это часть кода.
 *
 * Главный экспорт: buildSceneObjectsForLevels(levels, options).
 *   options.startId — стартовый id для генерируемых примитивов (по умолчанию 30000).
 *
 * Возвращает { primitives: [], blocks: [] }.
 */

let _idCounter = 0;
function makeIdAlloc(start) { _idCounter = start; return () => ++_idCounter; }

function basePrim(over) {
  return Object.assign({
    id: 0, x: 0, y: 0, z: 0,
    sx: 1, sy: 1, sz: 1, rx: 0, ry: 0, rz: 0,
    mass: 0, name: '', type: 'box', color: '#ffffff',
    visible: true, material: 'plastic',
    anchored: true, canCollide: false, folderId: null,
  }, over || {});
}

function makeSpike(nid, x) {
  return [basePrim({
    id: nid(), type: 'cone', x, y: 1.35, z: 0,
    sx: 0.9, sy: 1.2, sz: 0.9,
    color: '#e94545', material: 'neon', name: 'spike',
  })];
}

function makeSpikeTop(nid, x) {
  return [basePrim({
    id: nid(), type: 'cone', x, y: 6.0, z: 0,
    sx: 0.9, sy: 1.2, sz: 0.9, rx: 180,
    color: '#a44', material: 'neon', name: 'spike_top',
  })];
}

function makeTramp(nid, x) {
  return [
    basePrim({ id: nid(), type: 'box', x, y: 1.01, z: 0, sx: 1.8, sy: 0.02, sz: 1.8,
               color: '#ffcc00', material: 'neon', name: 'tramp_glow' }),
    basePrim({ id: nid(), type: 'box', x, y: 1.15, z: 0, sx: 1.8, sy: 0.3, sz: 1.8,
               color: '#ff9533', material: 'neon', name: 'tramp' }),
  ];
}

function makeSpeedPad(nid, x) {
  return [
    basePrim({ id: nid(), type: 'box', x, y: 1.01, z: 0, sx: 1.8, sy: 0.02, sz: 1.8,
               color: '#33ddff', material: 'neon', name: 'speedpad_glow' }),
    basePrim({ id: nid(), type: 'box', x, y: 1.10, z: 0, sx: 1.8, sy: 0.2, sz: 1.8,
               color: '#55ccff', material: 'neon', name: 'speedpad' }),
  ];
}

function makeJumpOrb(nid, x, y) {
  const innerId = nid();
  return {
    items: [
      basePrim({ id: innerId, type: 'sphere', x, y, z: 0,
                 sx: 0.9, sy: 0.9, sz: 0.9,
                 color: '#ffe44a', material: 'neon', name: 'jumporb' }),
      basePrim({ id: nid(), type: 'sphere', x, y, z: 0,
                 sx: 1.3, sy: 1.3, sz: 1.3,
                 color: '#ffaa00', material: 'neon', name: 'jumporb_glow' }),
    ],
    primId: innerId,
  };
}

function makeGravityFlip(nid, x) {
  const pid = nid();
  return {
    items: [
      basePrim({ id: pid, type: 'box', x, y: 3.5, z: 0, sx: 0.4, sy: 4.5, sz: 2.2,
                 color: '#b266ff', material: 'neon', name: 'gravity_portal' }),
      basePrim({ id: nid(), type: 'box', x, y: 3.5, z: 0, sx: 0.45, sy: 4.7, sz: 0.5,
                 color: '#ffffff', material: 'neon', name: 'gravity_portal_glow' }),
    ],
    primId: pid,
  };
}

function makeMoving(nid, x, yMin) {
  const pid = nid();
  return {
    items: [basePrim({
      id: pid, type: 'box', x, y: yMin, z: 0, sx: 2.5, sy: 0.5, sz: 2,
      color: '#22cc88', material: 'plastic', name: 'movplat',
      canCollide: true, anchored: true,
    })],
    primId: pid,
  };
}

function makeStep(nid, x, y, sx = 2, sy = 0.5) {
  return [basePrim({
    id: nid(), type: 'box', x, y, z: 0, sx, sy, sz: 2,
    color: '#5a8aff', material: 'plastic', name: 'step',
    canCollide: true, anchored: true,
  })];
}

function makeCeilingBlock(nid, x, y, length) {
  return [basePrim({
    id: nid(), type: 'box', x, y, z: 0, sx: length, sy: 1, sz: 3,
    color: '#446', material: 'plastic', name: 'ceiling',
    canCollide: true, anchored: true,
  })];
}

/** Сгенерировать воксельный пол для уровня (cotton-green верх + 4 stone). */
function addFloorBlocks(blocks, sx, finishX, holes) {
  const inHole = (col) => {
    for (const [a, b] of holes) if (col >= a && col < b) return true;
    return false;
  };
  let count = 0;
  for (let dx = 0; dx <= (finishX - sx) + 4; dx++) {
    if (inHole(dx)) continue;
    const wx = sx + dx;
    for (const z of [-1, 0, 1]) {
      blocks.push({ x: wx, y: 0, z, mass: 1, type: 'cotton-green',
                    visible: true, anchored: true, folderId: null, canCollide: true });
      for (const y of [-1, -2, -3, -4]) {
        blocks.push({ x: wx, y, z, mass: 1, type: 'stone',
                      visible: true, anchored: true, folderId: null, canCollide: true });
      }
      count += 5;
    }
  }
  return count;
}

/**
 * Главная функция. Принимает массив уровней, возвращает примитивы и блоки.
 * Не трогает декор (z=13/21/22/35/50) — это отдельная функция (см. ниже).
 */
export function buildSceneObjectsForLevels(levels, options = {}) {
  const nid = makeIdAlloc(options.startId || 30000);
  const primitives = [];
  const blocks = [];
  const updatedLevels = [];

  for (const L of levels) {
    const sx = L.spawnX || 0;
    const width = L.width || 240;
    const finishX = (L.finishX != null) ? L.finishX : (sx + width - 5);
    const holes = L.holes || [];

    // ПОЛ
    addFloorBlocks(blocks, sx, finishX, holes);

    // СТУПЕНЬКИ
    for (const st of (L.steps || [])) {
      primitives.push(...makeStep(nid, st.x, st.y, st.sx || 2, st.sy || 0.5));
    }

    // ШИПЫ
    for (const dx of (L.spikes || [])) {
      primitives.push(...makeSpike(nid, sx + dx));
    }
    for (const dx of (L.spikesTop || [])) {
      primitives.push(...makeSpikeTop(nid, sx + dx));
    }

    // ТРАМПЛИНЫ
    const trampsForScript = [];
    for (const t of (L.tramps || [])) {
      const rel = typeof t === 'number' ? t : (t.x != null ? t.x - sx : 0);
      trampsForScript.push(rel);
      primitives.push(...makeTramp(nid, sx + rel));
    }

    // SPEED PADS
    const padsForScript = [];
    for (const p of (L.speedPads || [])) {
      const rel = typeof p === 'number' ? p : (p.x != null ? p.x - sx : 0);
      padsForScript.push(rel);
      primitives.push(...makeSpeedPad(nid, sx + rel));
    }

    // JUMP ORBS
    const orbsForScript = [];
    for (const orb of (L.jumpOrbs || [])) {
      const wx = orb.worldX != null ? orb.worldX : sx + (orb.x || 0);
      const wy = orb.worldY != null ? orb.worldY : (orb.y || 3.5);
      const { items, primId } = makeJumpOrb(nid, wx, wy);
      primitives.push(...items);
      orbsForScript.push({ primId, worldX: wx, worldY: wy });
    }

    // GRAVITY FLIPS
    const flipsForScript = [];
    for (const gf of (L.gravityFlips || [])) {
      const wx = gf.worldX != null ? gf.worldX : sx + (gf.x || 0);
      const { items, primId } = makeGravityFlip(nid, wx);
      primitives.push(...items);
      flipsForScript.push({ primId, worldX: wx });
    }

    // CEILINGS (отрезки 4-блоков)
    for (const cl of (L.ceilings || [])) {
      const wx0 = cl.worldX != null ? cl.worldX : sx + (cl.x || 0);
      const len = cl.length || 4;
      const ceilingY = L.ceilingY || 7;
      for (let i = 0; i < len; i += 4) {
        primitives.push(...makeCeilingBlock(nid, wx0 + i + 2, ceilingY, 4));
      }
    }

    // MOVING PLATFORMS
    const movingForScript = [];
    for (const mp of (L.movingPlatforms || [])) {
      const wx = mp.worldX != null ? mp.worldX : sx + (mp.x || 0);
      const yMin = mp.yMin != null ? mp.yMin : 1.0;
      const yMax = mp.yMax != null ? mp.yMax : 2.5;
      const period = mp.period != null ? mp.period : 3.0;
      const { items, primId } = makeMoving(nid, wx, yMin);
      primitives.push(...items);
      movingForScript.push({
        primId, worldX: wx, yMin, yMax, period,
        phase: mp.phase != null ? mp.phase : Math.random() * 6.28,
      });
    }

    updatedLevels.push({
      id: L.id,
      name: L.name || `Уровень ${L.id}`,
      width,
      spawnX: sx,
      finishX,
      holes,
      spikes: L.spikes || [],
      spikesTop: L.spikesTop || [],
      tramps: trampsForScript,
      speedPads: padsForScript,
      movingPlatforms: movingForScript,
      jumpOrbs: orbsForScript,
      gravityFlips: flipsForScript,
      ceilings: L.ceilings || [],
      steps: L.steps || [],
      doubleJump: !!L.doubleJump,
      ceilingY: L.ceilingY || null,
    });
  }

  return { primitives, blocks, updatedLevels };
}
