// perf-test.mjs – misst FPS (und optional Heap-Δ) unter Last: schweres Level,
// viele Türme, aktive Welle. Nutzt die öffentliche window.__AEGIS_DEBUG__-API.
//
// Copyright (C) 2026 Lila-Kuh
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
// WICHTIG: Global-scope Identifier (TOWER_TYPES, Game, placeTower) müssen in
// page.evaluate() als BARE IDENTIFIER referenziert werden (nicht window.X).
//
// Usage: node perf-test.mjs [durationMs=15000] [towers=50]
import { chromium } from 'playwright';
import path from 'path';
import fs from 'fs';
import url from 'url';

const HERE = path.dirname(url.fileURLToPath(import.meta.url));
const ROOT = path.resolve(HERE, '..');
const HTML = path.resolve(ROOT, 'Aegis-Labyrinth.html').replace(/\\/g, '/');
const DURATION_MS = Math.max(1000, parseInt(process.argv[2] || '15000', 10));
const TOWERS = Math.max(1, parseInt(process.argv[3] || '50', 10));

const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
page.on('pageerror', (e) => console.error('[PAGE ERROR]', e.message));

await page.goto('file:///' + HTML, { waitUntil: 'load' });
await page.waitForFunction(() => window.__AEGIS_DEBUG__ !== undefined, { timeout: 15000 });

// Setup: schweres Level (letztes), Credits, Türme, Welle.
const setup = await page.evaluate(({ towers }) => {
  const d = window.__AEGIS_DEBUG__;
  const levelIdx = Math.min(LEVELS.length - 1, 10); // Level 11 (0-basiert Index 10)
  d.selectLevel(levelIdx);
  if (Game.state !== 'playing') Game.state = 'playing';
  d.setCredits(9999999);

  // Pfad & buildbare Zellen ermitteln.
  const path = [];
  for (let y = 0; y < MAP_H; y++)
    for (let x = 0; x < MAP_W; x++) if (Game.grid[y][x] === 1) path.push([x, y]);

  const towerTypes = TOWER_TYPES.filter(isTowerAvailable).filter((t) => isTowerUnlocked(t));
  const candidates = [];
  for (let y = 0; y < MAP_H; y++)
    for (let x = 0; x < MAP_W; x++) {
      if (Game.grid[y][x] !== 0) continue;
      let best = Infinity;
      for (const [px, py] of path) best = Math.min(best, Math.hypot(px - x, py - y));
      if (best < CELL * 3.01) candidates.push({ x, y, best });
    }
  candidates.sort((a, b) => a.best - b.best);

  let placed = 0;
  for (let i = 0; i < candidates.length && placed < towers; i++) {
    const def = towerTypes[i % Math.max(1, towerTypes.length)];
    if (!def) break;
    if (placeTower(def.id, candidates[i].x, candidates[i].y) === true) placed++;
  }
  d.startWave();
  const s = d.getState();
  return { levelIdx, levelName: s.levelName, placed, enemies: s.enemies, state: s.state };
}, { towers: TOWERS });
console.log(`Setup: Level=${setup.levelName} (idx ${setup.levelIdx}) · placed=${setup.placed} · enemies=${setup.enemies} · state=${setup.state}`);

// FPS messen: zähle requestAnimationFrame-Callbacks über DURATION_MS.
const fps = await page.evaluate((durationMs) => {
  return new Promise((resolve) => {
    let frames = 0;
    const start = performance.now();
    function tick() {
      frames++;
      if (performance.now() - start < durationMs) {
        requestAnimationFrame(tick);
      } else {
        const elapsed = (performance.now() - start) / 1000;
        resolve(Math.round(frames / elapsed));
      }
    }
    requestAnimationFrame(tick);
  });
}, DURATION_MS);

// Heap-Δ (falls available – Chromium performance.memory).
const heap = await page.evaluate(() => {
  const m = (typeof performance !== 'undefined' && performance.memory) ? performance.memory : null;
  return m
    ? { usedMB: +(m.usedJSHeapSize / 1048576).toFixed(1), totalMB: +(m.totalJSHeapSize / 1048576).toFixed(1) }
    : null;
});

const result = {
  timestamp: new Date().toISOString(),
  durationMs: DURATION_MS,
  fps,
  targetFps: 55,
  pass: fps >= 55,
  heap,
  setup,
};
const outFile = path.join(ROOT, 'perf-result.json');
fs.writeFileSync(outFile, JSON.stringify(result, null, 2));
const icon = result.pass ? '✅' : '❌';
console.log(`${icon} Average FPS: ${result.fps} (target ≥ ${result.targetFps})`);
if (heap) console.log(`   Heap: used=${heap.usedMB} MB`);
console.log(`✅ Perf done → ${outFile}`);

await browser.close();
process.exitCode = result.pass ? 0 : 1;