/**
 * Covers the HTML export's VIDEO path (perf/stability fix P4). The export used to
 * copy every original — including full-size videos — into a temp dir and then re-read
 * them into the ZIP (transient 2× disk). It now streams video originals straight from
 * disk into the archive. The image-only export specs never exercised that branch, so
 * this drives a real video upload → export → and proves the video entry lands in
 * Memories.zip (i.e. the streamed-from-original path works and the video isn't dropped).
 *
 * NOTE ON A PREVIOUS VERSION OF THIS COMMENT. It used to read: "The fixture clip is <1s, so ffmpeg
 * extracts no thumbnail frame — but exits 0, so the compression worker keeps the upload. That's the
 * intended shape here." None of that was intended. `-ss` sat AFTER `-i` (an output-side seek), so
 * against `sample.mp4` — which is exactly 1.000 s — ffmpeg exited 0 having written nothing, and
 * both the worker and the export gated on the exit status. The missing thumbnail was observed here
 * and written down as expected behaviour instead of investigated; every video test in the suite ran
 * against that one boundary fixture, and none of them ever fetched the poster.
 *
 * The seek is now input-side with a 0 s fallback and the artifact is verified rather than the exit
 * code, so this clip DOES get a thumbnail. The assertion at the bottom pins that.
 */
import { test, expect } from '../../fixtures/test';
import { uploadRaw } from '../../helpers/upload-client';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { BASE } from '../../helpers/env';

test.describe('Export — video streaming (P4)', () => {
  test('a real video upload is streamed into Memories.zip (not dropped)', async ({ host }) => {
    test.setTimeout(60_000);
    const bearer = { Authorization: `Bearer ${host.jwt}` };

    // Seed a real video upload owned by the host.
    const mp4 = readFileSync(join(process.cwd(), 'fixtures', 'media', 'sample.mp4'));
    const up = await uploadRaw(host.jwt, mp4, { filename: 'clip.mp4', contentType: 'video/mp4' });
    expect(up.status).toBe(201);
    const { id } = await up.json();

    // Release the gallery → spawns the real zip + html export jobs.
    const rel = await fetch(`${BASE}/api/v1/host/gallery/release`, {
      method: 'POST',
      headers: bearer,
    });
    expect(rel.status).toBe(204);

    // Wait for the HTML (Memories.zip) job — that's the one whose media staging P4 changed.
    await expect
      .poll(
        async () => {
          const res = await fetch(`${BASE}/api/v1/export/status`, { headers: bearer });
          return (await res.json()).html?.status;
        },
        { timeout: 45_000, intervals: [500] }
      )
      .toBe('done');

    // Download Memories.zip via the gated single-use ticket.
    const ticketRes = await fetch(`${BASE}/api/v1/export/ticket?kind=html`, {
      method: 'POST',
      headers: bearer,
    });
    const { ticket } = await ticketRes.json();
    const dl = await fetch(`${BASE}/api/v1/export/html?ticket=${encodeURIComponent(ticket)}`);
    expect(dl.status).toBe(200);

    const bytes = new Uint8Array(await dl.arrayBuffer());
    // Valid ZIP (PK local-file-header magic).
    expect(Array.from(bytes.subarray(0, 2))).toEqual([0x50, 0x4b]);

    // ZIP entry names are stored verbatim (uncompressed) in the archive, so the video's
    // media entry name appears as plaintext bytes. Its presence means the streamed
    // video entry was written; if the stream had failed, entry.close() would have
    // errored and the job would never have reached 'done'.
    const needle = `media/${id}.mp4`;
    const haystack = new TextDecoder('latin1').decode(bytes);
    expect(haystack.includes(needle), `Memories.zip must contain ${needle}`).toBe(true);

    // And its poster is really in the archive. This clip is 1.000 s — the exact case the old
    // output-side seek produced nothing for, silently, while `data.json` still advertised the
    // entry. See the note at the top of this file.
    expect(
      haystack.includes(`media/${id}_thumb.jpg`),
      `Memories.zip must contain the poster for ${id}, not just reference it`
    ).toBe(true);
  });
});
