import assert from 'node:assert/strict';
import fssync from 'node:fs';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';

import { ZipFile } from 'yazl';

import { PROJECT_SCHEMA_VERSION } from '../../shared/types';

import { readProjectJsonFromZip } from './yauzlProjectZip';

void test('readProjectJsonFromZip: sequential reads close yauzl (no EMFILE)', async () => {
  const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'dnd-zip-read-'));
  const zipPath = path.join(tmp, 'test.ttrpg.zip');
  const minimal = {
    id: 'p1',
    meta: {
      name: 'n',
      fileBaseName: 'f',
      createdAt: '2020-01-01',
      updatedAt: '2020-01-01',
      createdWithAppVersion: '1',
      appVersion: '1',
      schemaVersion: PROJECT_SCHEMA_VERSION,
    },
    scenes: {},
    assets: {},
    currentSceneId: null,
    currentGraphNodeId: null,
    sceneGraphNodes: [],
    sceneGraphEdges: [],
  };

  const zipfile = new ZipFile();
  zipfile.addBuffer(Buffer.from(JSON.stringify(minimal), 'utf8'), 'project.json', { compressionLevel: 9 });
  const out = fssync.createWriteStream(zipPath);
  const done = new Promise<void>((resolve, reject) => {
    out.on('close', resolve);
    out.on('error', reject);
  });
  zipfile.outputStream.pipe(out);
  zipfile.end();
  await done;

  for (let i = 0; i < 150; i += 1) {
    const p = await readProjectJsonFromZip(zipPath);
    assert.equal(p.id, 'p1');
  }

  await fs.rm(tmp, { recursive: true, force: true });
});

void test('readProjectJsonFromZip: campaignAudios round-trips inside project.json', async () => {
  const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'dnd-zip-campaign-'));
  const zipPath = path.join(tmp, 'test.ttrpg.zip');
  const assetId = 'audio_asset_1';
  const minimal = {
    id: 'p1',
    meta: {
      name: 'n',
      fileBaseName: 'f',
      createdAt: '2020-01-01',
      updatedAt: '2020-01-01',
      createdWithAppVersion: '1',
      appVersion: '1',
      schemaVersion: PROJECT_SCHEMA_VERSION,
    },
    scenes: {},
    assets: {},
    campaignAudios: [{ assetId, autoplay: true, loop: false }],
    currentSceneId: null,
    currentGraphNodeId: null,
    sceneGraphNodes: [],
    sceneGraphEdges: [],
  };

  const zipfile = new ZipFile();
  zipfile.addBuffer(Buffer.from(JSON.stringify(minimal), 'utf8'), 'project.json', { compressionLevel: 9 });
  const out = fssync.createWriteStream(zipPath);
  const done = new Promise<void>((resolve, reject) => {
    out.on('close', resolve);
    out.on('error', reject);
  });
  zipfile.outputStream.pipe(out);
  zipfile.end();
  await done;

  const p = await readProjectJsonFromZip(zipPath);
  assert.ok(Array.isArray((p as { campaignAudios?: unknown }).campaignAudios));
  assert.deepEqual(
    (p as { campaignAudios: typeof minimal.campaignAudios }).campaignAudios,
    minimal.campaignAudios,
  );

  await fs.rm(tmp, { recursive: true, force: true });
});
