// report.mjs – erzeugt report.html aus audit-result.json, perf-result.json,
// selfplay-results.json (falls vorhanden).
// Pass/Fail wird direkt aus den audit.assertions und audit.summary gelesen
// (authoritative Quelle aus audit.mjs), nicht mit eigenen Heuristiken.
//
// 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/>.
//
// Usage: node report.mjs
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 auditPath = path.join(ROOT, 'audit-result.json');
const perfPath  = path.join(ROOT, 'perf-result.json');
const selfPath  = path.join(ROOT, 'selfplay-results.json');

function loadJson(p) {
  try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; }
}

const audit    = loadJson(auditPath);
const perf     = loadJson(perfPath);
const selfplay = loadJson(selfPath);

function statusColor(ok) { return ok ? '#4caf50' : '#f44336'; }

/** true wenn Liste nicht leer ist und alle Assertions `passed: true` haben */
function allPass(list) {
  return Array.isArray(list) && list.length > 0 && list.every(a => a.passed === true);
}

/** Anzahl der bestanden Assertions in einer Liste */
function countPassed(list) {
  return Array.isArray(list) ? list.filter(a => a.passed === true).length : 0;
}

// ─── Audit Summary ────────────────────────────────────────────────────────────
let auditRows = '';
let auditPass = false;

if (audit) {
  const selfA = audit.assertions?.self      ?? [];
  const extA  = audit.assertions?.extension ?? [];
  const redA  = audit.assertions?.redesign  ?? [];

  // Assertions aus audit.mjs nach Themen gruppieren (Name-Prefix)
  const conn = selfA.filter(a => /^L\d+ /.test(a.name));
  const atk  = selfA.filter(a => a.name.startsWith('tower '));
  const skl  = selfA.filter(a => a.name.startsWith('skill '));
  const plc  = selfA.filter(a => a.name.startsWith('placement'));

  const rows = [
    ['Connectivity', allPass(conn),
      `${countPassed(conn)}/${conn.length} level checks`],
    ['Attacks', allPass(atk),
      `${countPassed(atk)}/${atk.length} tower checks (inkl. Support/Relay)`],
    ['Skills', allPass(skl),
      `${countPassed(skl)}/${skl.length} skills · rank ≥ 1`],
    ['Placement', allPass(plc),
      audit.self?.placement
        ? `wall=${audit.self.placement.wallAccepted} path=${audit.self.placement.pathAccepted}`
        : 'N/A'],
  ];

  if (extA.length > 0) {
    rows.push(['Extension (Mortar/Tesla/Blocker)', allPass(extA),
      `${countPassed(extA)}/${extA.length} checks`]);
  }
  if (redA.length > 0) {
    rows.push(['Redesign (Kryo/Sniper/Quantum/Nano)', allPass(redA),
      `${countPassed(redA)}/${redA.length} checks`]);
  }

  auditRows = rows.map(([name, ok, detail]) =>
    `<tr><td>${name}</td>` +
    `<td style="color:${statusColor(ok)};font-weight:bold">${ok ? '✅ PASS' : '❌ FAIL'}</td>` +
    `<td>${detail}</td></tr>`
  ).join('\n');

  // Gesamtverdict: authoritative audit.summary + pageErrors
  const pageErrors = audit.pageErrors?.length ?? 1;
  auditPass = (audit.summary?.fail ?? -1) === 0 && pageErrors === 0;
} else {
  auditRows = '<tr><td colspan="3" style="color:#999">audit-result.json not found</td></tr>';
}

// ─── Perf Summary ─────────────────────────────────────────────────────────────
let perfRow = '';
let perfOk = false;
if (perf) {
  perfOk = perf.pass === true;
  perfRow = `<tr><td>FPS</td>` +
    `<td style="color:${statusColor(perfOk)};font-weight:bold">${perf.fps} fps</td>` +
    `<td>target ≥ ${perf.targetFps} · duration ${perf.durationMs}ms</td></tr>`;
  if (perf.heap) perfRow +=
    `<tr><td>Heap</td><td style="color:#4caf50">${perf.heap.usedMB} MB</td>` +
    `<td>total ${perf.heap.totalMB} MB</td></tr>`;
} else {
  perfRow = '<tr><td colspan="3" style="color:#999">perf-result.json not found</td></tr>';
}

// ─── Self-Play Summary ────────────────────────────────────────────────────────
let selfRows = '';
let selfOk = false;
if (selfplay) {
  const s = selfplay.summary;
  selfOk = s.winRate >= 50;
  selfRows = `
    <tr><td>Episodes</td><td style="color:#4caf50">${s.total}</td><td>winRate ${s.winRate.toFixed(1)}%</td></tr>
    <tr><td>Wins</td><td style="color:${statusColor(selfOk)};font-weight:bold">${s.wins}</td><td>Losses: ${s.losses}</td></tr>
    <tr><td>Avg Kills</td><td style="color:#4caf50">${s.avgKills}</td><td>Avg Wave: ${s.avgWaves}</td></tr>
  `;
} else {
  selfRows = '<tr><td colspan="3" style="color:#999">selfplay-results.json not found (nightly only)</td></tr>';
}

const overallPass = auditPass && perfOk && (selfplay ? selfOk : true);

// ─── HTML ─────────────────────────────────────────────────────────────────────
const html = `<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Aegis Labyrinth – CI Report</title>
<style>
  body{font-family:'Segoe UI',system-ui,monospace;background:#0d1117;color:#c9d1d9;padding:30px;margin:0}
  h1{font-size:1.6em;margin-bottom:4px}
  .ts{color:#8b949e;font-size:.85em;margin-bottom:24px}
  .badge{display:inline-block;padding:4px 14px;border-radius:12px;font-weight:bold;font-size:.9em;margin-bottom:20px}
  .pass{background:#1a3a2a;color:#4caf50}.fail{background:#3a1a1a;color:#f44336}
  h2{font-size:1.1em;margin:24px 0 8px;color:#8b949e;text-transform:uppercase;letter-spacing:1px}
  table{border-collapse:collapse;width:100%;margin-bottom:20px}
  th,td{padding:10px 14px;text-align:left;border-bottom:1px solid #21262d}
  th{background:#161b22;color:#8b949e;font-size:.85em;text-transform:uppercase;letter-spacing:.5px}
  tr:hover{background:#161b22}
  .footer{color:#484f58;font-size:.8em;margin-top:40px;border-top:1px solid #21262d;padding-top:16px}
</style>
</head>
<body>
<h1>⚔️ Aegis Labyrinth – CI Report</h1>
<p class="ts">Timestamp: ${new Date().toISOString()} · Branch: <code>unknown</code></p>
<span class="badge ${overallPass ? 'pass' : 'fail'}">${overallPass ? '✅ ALL CHECKS PASSED' : '❌ SOME CHECKS FAILED'}</span>

<h2>Audits</h2>
<table>
<tr><th>Check</th><th>Status</th><th>Detail</th></tr>
${auditRows}
</table>

<h2>Performance</h2>
<table>
<tr><th>Metric</th><th>Value</th><th>Info</th></tr>
${perfRow}
</table>

<h2>Self-Play</h2>
<table>
<tr><th>Metric</th><th>Value</th><th>Info</th></tr>
${selfRows}
</table>

<div class="footer">
  Generated by <code>report.mjs</code> · Aegis Labyrinth v${audit ? '1.0.2' : '?'} · ${new Date().toLocaleString('de-DE')}
</div>
</body>
</html>`;

const reportPath = path.join(ROOT, 'report.html');
fs.writeFileSync(reportPath, html);
console.log(`✅ Report generated → ${reportPath} (${overallPass ? 'ALL PASS' : 'SOME FAILURES'})`);
