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

import { createServer } from "vite";

let server;
let fetchRecordedMediaArchive;
let recordedMediaPresentationState;
let recordedMediaSeekableCoverage;

before(async () => {
  server = await createServer({
    appType: "custom",
    logLevel: "silent",
    server: { middlewareMode: true },
  });
  ({
    fetchRecordedMediaArchive,
    recordedMediaPresentationState,
    recordedMediaSeekableCoverage,
  } = await server.ssrLoadModule("/src/components/RecordedFmp4Player.tsx"));
});

after(async () => {
  await server?.close();
});

function fixture({ byteLength = 36_000_000_000 } = {}) {
  const manifestUrl = "/api/v1/observation-sessions/session-1/media/camera-1/manifest";
  const generation = "a".repeat(64);
  const streamUrl = manifestUrl.replace(
    "/manifest",
    `/epochs/1/recording.mp4?generation=${generation}`,
  );
  const manifest = {
    schema_version: "missioncore.observation-recorded-media/v3",
    source_id: "recorded.camera.camera-1",
    generation_sha256: generation,
    byte_length: byteLength,
    timeline_start_seconds: 0,
    timeline_end_seconds: 36_000,
    synchronization: "host-arrival-best-effort",
    epochs: [{
      ordinal: 1,
      timeline_start_seconds: 0,
      timeline_end_seconds: 36_000,
      media_type: 'video/mp4; codecs="avc1.640028"',
      byte_length: byteLength,
      stream_url: streamUrl,
    }],
  };
  const source = {
    id: manifest.source_id,
    label: "Записанная камера",
    modality: "video",
    manifestUrl,
    manifestGenerationSha256: generation,
    byteLength,
    mediaType: "video/mp4",
    timelineStartSeconds: 0,
    timelineEndSeconds: 36_000,
    seekable: true,
    synchronization: "host-arrival-best-effort",
  };
  return { source, manifest, generation, streamUrl };
}

function jsonResponse(payload, generation) {
  return new Response(JSON.stringify(payload), {
    status: 200,
    headers: {
      "Content-Type": "application/json",
      ETag: `"sha256:${generation}"`,
    },
  });
}

test("multi-hour camera admission fetches only its compact generation-bound manifest", async () => {
  const { source, manifest, generation, streamUrl } = fixture();
  const requested = [];
  const archive = await fetchRecordedMediaArchive(source, {
    fetcher: async (input, request = {}) => {
      requested.push(String(input));
      assert.equal(new Headers(request.headers).get("If-Match"), `"sha256:${generation}"`);
      return jsonResponse(manifest, generation);
    },
  });

  assert.deepEqual(requested, [source.manifestUrl]);
  assert.equal(archive.byteLength, 36_000_000_000);
  assert.equal(archive.manifest.epochs[0].streamUrl, streamUrl);
});

test("first camera manifest request rejects a replaced generation", async () => {
  const { source, manifest, generation } = fixture();
  const replacementGeneration = "d".repeat(64);
  await assert.rejects(
    fetchRecordedMediaArchive(source, {
      fetcher: async (_input, request = {}) => {
        assert.equal(new Headers(request.headers).get("If-Match"), `"sha256:${generation}"`);
        return jsonResponse(
          { ...manifest, generation_sha256: replacementGeneration },
          replacementGeneration,
        );
      },
    }),
    /несовместим|заменён/,
  );
});

test("camera manifest fails closed when epoch bytes do not match launch bytes", async () => {
  const { source, manifest, generation } = fixture();
  const mismatched = {
    ...manifest,
    epochs: [{ ...manifest.epochs[0], byte_length: manifest.byte_length - 1 }],
  };
  await assert.rejects(
    fetchRecordedMediaArchive(source, {
      fetcher: async () => jsonResponse(mismatched, generation),
    }),
    /не совпадает/,
  );
});

test("camera presentation gate opens only for the seekable selected epoch", () => {
  assert.equal(recordedMediaPresentationState("loading", null, "g1", false), "loading");
  assert.equal(recordedMediaPresentationState("ready", null, "g1", false), "loading");
  assert.equal(recordedMediaPresentationState("ready", "g2", "g1", false), "loading");
  assert.equal(recordedMediaPresentationState("ready", "g1", "g1", false), "ready");
  assert.equal(recordedMediaPresentationState("ready", "g1", null, true), "waiting");
  assert.equal(recordedMediaPresentationState("error", "g1", "g1", false), "error");
  assert.equal(recordedMediaPresentationState("ready", "g1", "g1", false, "loading"), "loading");
  assert.equal(recordedMediaPresentationState("ready", "g1", null, true, "loading"), "loading");
  assert.equal(recordedMediaPresentationState("ready", "g1", "g1", false, "error"), "error");
});

test("decoded duration and seekable range cover the complete declared epoch", () => {
  assert.equal(recordedMediaSeekableCoverage(20, 20, 20), true);
  assert.equal(recordedMediaSeekableCoverage(19, 19, 20), true);
  assert.equal(recordedMediaSeekableCoverage(18.99, 20, 20), false);
  assert.equal(recordedMediaSeekableCoverage(20, 18.99, 20), false);
  assert.equal(recordedMediaSeekableCoverage(20, 20, 20, 1, 1), true);
  assert.equal(recordedMediaSeekableCoverage(20, 20, 20, 1, 1.01), false);
});

test("recorded player range-streams and never builds a whole-video RAM Blob", async () => {
  const source = await readFile(
    new URL("../src/components/RecordedFmp4Player.tsx", import.meta.url),
    "utf8",
  );
  assert.match(source, /video\.src\s*=\s*descriptor\.streamUrl/);
  assert.doesNotMatch(source, /new Blob\(|response\.arrayBuffer\(|SourceBuffer/);
});

test("loading and error overlays fully conceal recorded camera pixels", async () => {
  const css = await readFile(
    new URL("../src/styles/observation.css", import.meta.url),
    "utf8",
  );
  assert.match(
    css,
    /\.recorded-media-player:not\(\[data-state="ready"\]\) \.observation-media__asset\s*\{[^}]*visibility:\s*hidden/s,
  );
  assert.match(
    css,
    /\.recorded-media-player__notice\s*\{[^}]*inset:\s*0;[^}]*background:\s*#070809/s,
  );
});

test("point-cloud fullscreen keeps the admitted recorded camera worker mounted", async () => {
  const source = await readFile(
    new URL("../src/workspaces/Workspaces.tsx", import.meta.url),
    "utf8",
  );
  assert.match(source, /\{visibleMediaSources\.map\(\(source, index\) => \(/);
  assert.match(source, /hidden=\{pointCloudFocused \|\| unifiedPerception\}/);
  assert.doesNotMatch(
    source,
    /\{!pointCloudFocused \? visibleMediaSources\.map/,
  );
  assert.doesNotMatch(
    source,
    /\(pointCloudFocused \|\| !observationLayout\.visibleSourceIds\.has\(source\.id\)\)/,
  );
});
