import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { createReadStream } from "node:fs";
import { access, readFile, stat } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import test from "node:test";
import { execFile } from "node:child_process";
import { promisify } from "node:util";

const execFileAsync = promisify(execFile);
const siteDirectory = resolve(dirname(fileURLToPath(import.meta.url)), "..");

const hashFile = async (file) => {
  const hash = createHash("sha256");
  for await (const chunk of createReadStream(file)) hash.update(chunk);
  return hash.digest("hex");
};

test("la release publiée correspond exactement à l’exécutable direct", async () => {
  const release = JSON.parse(await readFile(resolve(siteDirectory, "release.json"), "utf8"));
  assert.match(release.fileName, /\.exe$/iu);
  assert.match(release.url, /\.exe$/iu);
  const artifact = resolve(siteDirectory, "downloads", release.fileName);
  const metadata = await stat(artifact);
  assert.equal(metadata.size, release.sizeBytes);
  assert.equal(await hashFile(artifact), release.sha256);
  assert.match(release.sha256, /^[a-f0-9]{64}$/u);
});

test("l'installateur et le ZIP source publiés sont eux aussi intègres", async () => {
  const release = JSON.parse(await readFile(resolve(siteDirectory, "release.json"), "utf8"));
  assert.equal(release.additionalDownloads.length, 2);
  for (const download of release.additionalDownloads) {
    const artifact = resolve(siteDirectory, "downloads", download.fileName);
    const metadata = await stat(artifact);
    assert.equal(metadata.size, download.sizeBytes);
    assert.equal(await hashFile(artifact), download.sha256);
    assert.match(download.url, /^\.\/downloads\//u);
  }
});

test("l'audit du launcher embarqué et la barrière de publication sont synchronisés", async () => {
  const result = await execFileAsync(process.execPath, ["scripts/audit-release.mjs", "--check"], {
    cwd: siteDirectory,
    maxBuffer: 2 * 1024 * 1024,
  });
  assert.match(result.stdout, /^(?:BLOCKED|PREVIEW|PUBLISHABLE) launcher v/u);
});

test("les ressources locales référencées par la page existent", async () => {
  const html = await readFile(resolve(siteDirectory, "index.html"), "utf8");
  const references = [...html.matchAll(/(?:href|src)="(\.\/[^"#?]+)"/gu)].map((match) => match[1]);
  assert.ok(references.length >= 7);
  await Promise.all(references.map((reference) => access(resolve(siteDirectory, reference))));
});

test("le CTA reflète l'état de publication et conserve les mentions essentielles", async () => {
  const [html, release] = await Promise.all([
    readFile(resolve(siteDirectory, "index.html"), "utf8"),
    readFile(resolve(siteDirectory, "release.json"), "utf8").then(JSON.parse),
  ]);
  assert.equal(html.includes('data-download-link'), true);
  if (!release.downloadEnabled) assert.equal(html.includes('aria-disabled="true"'), true);
  assert.match(html, /Produit indépendant, non approuvé ni associé à Mojang ou Microsoft/u);
  assert.match(html, /game\.bglands\.fr/u);
  assert.match(html, /19134/u);
  assert.match(html, /25565/u);
});

test("le port Bedrock public affiché correspond au déploiement Compose", async () => {
  const [html, compose] = await Promise.all([
    readFile(resolve(siteDirectory, "index.html"), "utf8"),
    readFile(resolve(siteDirectory, "..", "compose.yml"), "utf8"),
  ]);
  const port = compose.match(/\$\{BEDROCK_PORT:-([0-9]+)\}:19132\/udp/u)?.[1];
  assert.equal(port, "19134");
  assert.ok(html.includes(`game.bglands.fr : ${port}`));
});

test("le site public utilise le sous-domaine launcher et conserve les ressources sur game", async () => {
  const [html, compose] = await Promise.all([
    readFile(resolve(siteDirectory, "index.html"), "utf8"),
    readFile(resolve(siteDirectory, "..", "compose.yml"), "utf8"),
  ]);
  assert.match(compose, /Host\(`launcher\.bglands\.fr`\)/u);
  assert.match(html, /<link rel="canonical" href="https:\/\/launcher\.bglands\.fr\/"/u);
  assert.match(html, /href="https:\/\/game\.bglands\.fr\/resources\/bglands-client\/release\.json"/u);
});
