import assert from "node:assert/strict";
import { access, readFile } from "node:fs/promises";
import test from "node:test";

const template_root = new URL("../", import.meta.url);

async function render() {
  const worker_url = new URL("../dist/server/index.js", import.meta.url);
  worker_url.searchParams.set("test", `${process.pid}-${Date.now()}`);
  const { default: worker } = await import(worker_url.href);

  return worker.fetch(
    new Request("http://localhost/", {
      headers: {
        accept: "text/html",
        host: "localhost",
      },
    }),
    {
      ASSETS: {
        fetch: async () => new Response("Not found", { status: 404 }),
      },
    },
    {
      waitUntil() {},
      passThroughOnException() {},
    },
  );
}

test("server-renders the Microkeeper motion playbook", async () => {
  const response = await render();
  assert.equal(response.status, 200);
  assert.match(response.headers.get("content-type") ?? "", /^text\/html\b/i);

  const html = await response.text();
  assert.match(html, /<title>Make the web move — Microkeeper Motion Playbook<\/title>/i);
  assert.match(html, /Make the web/);
  assert.match(html, /Hire to retire/);
  assert.match(html, /AI agents showcase/);
  assert.match(html, /What to do/);
  assert.match(html, /Starting prompt/);
  assert.doesNotMatch(html, /breakthrough|The creative rule|70% product truth|decision that made/i);
  assert.doesNotMatch(html, /codex-preview|Your site is taking shape|react-loading-skeleton/i);
});

test("ships both films, the prompt and adapted resources", async () => {
  const required_files = [
    "public/videos/hire-to-retire/index.html",
    "public/videos/hire-to-retire/hire-to-retire.mp4",
    "public/videos/hire-to-retire/assets/microkeeper-motion-bgm.mp3",
    "public/videos/hire-to-retire/assets/microkeeper-voiceover.wav",
    "public/videos/ai-agents/index.html",
    "public/videos/ai-agents/motion.css",
    "public/videos/ai-agents/motion.js",
    "public/videos/ai-agents/agent-os-motion-showcase.mp4",
    "public/resources/microkeeper-motion-starter-prompt.txt",
    "public/resources/brand-context-template.md",
    "public/resources/browser-motion-technique-prompt-library.md",
    "public/resources/assets/brand/microkeeper-landscape-white.svg",
    "public/resources/assets/brand/microkeeper-landscape-red.svg",
    "public/resources/assets/brand/microkeeper-icon-red.svg",
    "public/resources/assets/brand/microkeeper-icon-white.svg",
    "public/og.png",
  ];

  await Promise.all(required_files.map((file) => access(new URL(file, template_root))));

  const [page, layout, styles, package_json] = await Promise.all([
    readFile(new URL("app/page.tsx", template_root), "utf8"),
    readFile(new URL("app/layout.tsx", template_root), "utf8"),
    readFile(new URL("app/globals.css", template_root), "utf8"),
    readFile(new URL("package.json", template_root), "utf8"),
  ]);

  assert.match(page, /window\.render_at_time\(seconds\)/);
  assert.match(page, /browser-motion-technique-prompt-library\.md/);
  assert.match(page, /\/videos\/hire-to-retire\/hire-to-retire\.mp4/);
  assert.match(page, /\/videos\/hire-to-retire\/assets\/dylan-wong\.jpg/);
  assert.doesNotMatch(page, /<img src="\/brand\/microkeeper-landscape-red\.svg"/);
  assert.match(page, /https:\/\/atlassian\.design\/DESIGN\.md/);
  assert.match(page, /Complete site source/);
  assert.match(styles, /aspect-ratio:\s*38\s*\/\s*25/);
  assert.match(styles, /width:\s*min\(100%,\s*calc\(152svh - 274px\),\s*1368px\)/);
  assert.doesNotMatch(styles, /\.prompt_section\s*\{\s*background:\s*var\(--red\)/);
  assert.match(layout, /\/og\.png/);
  assert.doesNotMatch(package_json, /react-loading-skeleton/);
  await assert.rejects(access(new URL("app/_sites-preview/SkeletonPreview.tsx", template_root)));
  await assert.rejects(access(new URL("public/resources/original-microkeeper-design-guide.md", template_root)));
  await assert.rejects(access(new URL("public/resources/microkeeper-motion-brand-brief.md", template_root)));
});

test("aligns narrated scene starts to the transcript cues", async () => {
  const [film_html, cue_text] = await Promise.all([
    readFile(new URL("public/videos/hire-to-retire/index.html", template_root), "utf8"),
    readFile(new URL("public/videos/hire-to-retire/narration-cues.json", template_root), "utf8"),
  ]);
  const cue_sheet = JSON.parse(cue_text);

  for (const cue of cue_sheet.cues.filter((item) => item.scene !== "hero_and_slogan")) {
    const match = film_html.match(
      new RegExp(`id: 'scene_${cue.scene}', start: ([0-9.]+), end:`),
    );
    assert.ok(match, `missing scene timing for ${cue.scene}`);
    const scene_start = Number(match[1]);
    const visual_lead = cue.timeline_start - scene_start;
    assert.ok(visual_lead >= 0, `${cue.scene} begins after its narration`);
    assert.ok(visual_lead <= 0.35, `${cue.scene} leads its narration by too much`);
  }

  assert.match(film_html, /voiceover\.currentTime\s*\/\s*voiceover_rate/);
  assert.match(film_html, /const module_cues = \[0, 1\.9, 2\.9, 3\.9\]/);
  assert.equal(cue_sheet.timeline_duration_seconds, 47.5);
});
