const assert = require("assert");
const fs = require("fs");
const path = require("path");

const root = path.join(__dirname, "..");
const { ladderFor, ffmpegArgs, streamTestingService } = require("../src/services/stream-testing");
const protocol = require("../plugins/lumi_transcription/backend/companion/protocol");

async function main() {
  const fullHd = ladderFor({ width: 1920, height: 1080, fps: 60 });
  assert.deepStrictEqual(fullHd.variants.map((item) => item.name), ["source", "720p", "480p"]);
  assert(fullHd.variants.every((item) => item.width <= 1920 && item.height <= 1080), "adaptive ladder must never upscale");
  const hd = ladderFor({ width: 1280, height: 720, fps: 30 });
  assert.deepStrictEqual(hd.variants.map((item) => item.name), ["source", "480p"]);
  const sd = ladderFor({ width: 854, height: 480, fps: 30 });
  assert.deepStrictEqual(sd.variants.map((item) => item.name), ["source"]);

  const fakeSession = {
    source: fullHd,
    receiver: { encoder: "h264_nvenc" },
    listenerUrl: "rtmp://0.0.0.0:19350/lumi-test/private-secret",
    directory: path.join(root, ".tmp-stream-test"),
  };
  const args = ffmpegArgs(fakeSession);
  assert(args.includes("h264_nvenc") && args.includes("master.m3u8"));
  assert(args.includes("v:0,a:0,name:source v:1,a:1,name:720p v:2,a:2,name:480p"));
  assert(args.includes("delete_segments+independent_segments+program_date_time+temp_file"));
  assert(args.includes("512") && args.includes("1024"), "receiver queues must be bounded");

  for (const type of ["stream_test_create", "stream_test_obs_metrics", "stream_test_caption", "stream_test_stop"]) {
    const parsed = protocol.parseEnvelope(Buffer.from(JSON.stringify(protocol.envelope(type, {}, null))));
    assert.strictEqual(parsed.type, type);
  }

  const server = fs.readFileSync(path.join(root, "src/web/server.js"), "utf8");
  const service = fs.readFileSync(path.join(root, "src/services/stream-testing.js"), "utf8");
  const gateway = fs.readFileSync(path.join(root, "plugins/lumi_transcription/backend/companion/gateway.js"), "utf8");
  const nativeBridge = fs.readFileSync(path.join(root, "companion/native/obs-bridge/src/plugin.cpp"), "utf8");
  const companion = fs.readFileSync(path.join(root, "companion/src/Lumi.Companion.App/CompanionRuntime.cs"), "utf8");
  const companionUi = fs.readFileSync(path.join(root, "companion/src/Lumi.Companion.App/MainWindow.axaml.cs"), "utf8");
  const webUi = fs.readFileSync(path.join(root, "src/web/views/admin-stream-testing.ejs"), "utf8");

  assert.match(server, /admin\/stream-testing", requireRole\("admin"\)/);
  assert.match(server, /admin\/stream-testing\/media\/:id\/:name", requireRole\("admin"\)/);
  assert.match(service, /MAX_SESSION_MS/);
  assert.match(service, /INACTIVITY_MS/);
  assert.match(service, /MAX_OUTPUT_BYTES/);
  assert.match(service, /redactDiagnostic/);
  assert.match(service, /caption_delay/);
  assert.match(service, /receiver_slow/);
  assert.match(service, /PUBLIC_INGEST_PORT/);
  assert.strictEqual(/^(?:master|stream_(?:source|720p|480p))(?:\.m3u8|_\d{6}\.ts)$/.test("master.m3u8"), true);
  assert.match(service, /spawn\(FFMPEG, args, \{[^}]*stdio:/s);
  assert.match(service, /testsrc2=size=1280x720:rate=30/);
  assert.match(service, /sine=frequency=880:sample_rate=48000/);
  assert(!service.includes("shell: true"), "receiver commands must not use a shell");
  assert.match(gateway, /stream_test_create/);
  assert.match(gateway, /Companion disconnected; the private receiver was stopped/);
  assert.match(nativeBridge, /stream_test_snapshot/);
  assert.match(nativeBridge, /obs_frontend_save_streaming_service/);
  assert.match(nativeBridge, /stream_test_restore/);
  assert.match(companion, /StreamTestRecoveryStore/);
  assert.match(companion, /StartStreamTestAsync/);
  assert.match(companion, /RestoreObsAfterStreamTestAsync/);
  assert.match(companion, /finally\s*\{\s*Interlocked\.Exchange\(ref _streamTestRestoreRunning, 0\)/s);
  assert.match(companionUi, /CheckUpdateButton\.IsEnabled = !state\.UpdateCheckRunning/);
  assert.match(companionUi, /CheckUpdateButton\.Content = state\.UpdateCheckRunning \? "Checking…" : "Check now"/);
  assert.match(webUi, /data-stream-player/);
  assert.match(webUi, /data-stream-quality/);
  assert.match(webUi, /data-stream-captions/);
  assert.match(webUi, /data-stream-timeline/);

  await streamTestingService.close();
  console.log("Stream testing and Companion lifecycle verification passed.");
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
