/**
 * Component: Metadata Tagging Integration Tests (real ffmpeg)
 * Documentation: documentation/phase3/file-organization.md
 *
 * Gated behind a runtime ffmpeg/ffprobe availability check. Skips cleanly
 * if either binary is missing. Verifies that tag values written via the
 * real argv-form spawn path are read back byte-for-byte by ffprobe — this
 * is the integration-layer guarantee that the quote-bug (#171) cannot recur.
 *
 * Note: availability check uses spawnSync at module load so `it.skipIf` can
 * see the result (test-registration is synchronous; beforeAll runs later).
 */

import { execFile, spawnSync } from 'child_process';
import { promisify } from 'util';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { tagAudioFileMetadata } from '@/lib/utils/metadata-tagger';

const execFileP = promisify(execFile);

function hasBinarySync(bin: string): boolean {
  try {
    const result = spawnSync(bin, ['-version'], { stdio: 'ignore' });
    return result.status === 0;
  } catch {
    return false;
  }
}

const ffmpegAvailable = hasBinarySync('ffmpeg');
const ffprobeAvailable = hasBinarySync('ffprobe');
const skipReal = !(ffmpegAvailable && ffprobeAvailable);

let workDir = '';

beforeAll(async () => {
  if (skipReal) {
    // eslint-disable-next-line no-console
    console.warn(
      `[metadata-tagger.integration] Skipping real-ffmpeg tests: ffmpeg=${ffmpegAvailable}, ffprobe=${ffprobeAvailable}`
    );
    return;
  }
  workDir = await fs.mkdtemp(path.join(os.tmpdir(), 'rmab-tagger-it-'));
}, 30_000);

afterAll(async () => {
  if (workDir) {
    try {
      await fs.rm(workDir, { recursive: true, force: true });
    } catch {
      // best-effort cleanup
    }
  }
});

/** Create a small silent fixture audio file in the requested format. */
async function makeFixture(ext: '.m4a' | '.mp3' | '.flac'): Promise<string> {
  const out = path.join(workDir, `fixture-${Date.now()}-${Math.random().toString(36).slice(2)}${ext}`);
  const codecArgs =
    ext === '.mp3'
      ? ['-c:a', 'libmp3lame', '-q:a', '9']
      : ext === '.flac'
        ? ['-c:a', 'flac']
        : ['-c:a', 'aac', '-b:a', '32k'];
  await execFileP('ffmpeg', [
    '-y',
    '-f', 'lavfi',
    '-i', 'anullsrc=channel_layout=mono:sample_rate=22050',
    '-t', '1',
    ...codecArgs,
    out,
  ]);
  return out;
}

/** Read a single metadata tag back from a file via ffprobe. */
async function readTag(filePath: string, key: string): Promise<string | undefined> {
  const { stdout } = await execFileP('ffprobe', [
    '-v', 'error',
    '-show_entries', 'format_tags',
    '-of', 'json',
    filePath,
  ]);
  const parsed = JSON.parse(stdout) as { format?: { tags?: Record<string, string> } };
  const tags = parsed.format?.tags ?? {};
  return tags[key] ?? tags[key.toLowerCase()] ?? tags[key.toUpperCase()];
}

describe('metadata-tagger integration (real ffmpeg)', () => {
  // Torture value covers the bug-class surface:
  //   embedded `"`, `'`, `$`, `` ` ``, `\`, em-dash.
  const TORTURE = `Test "Quoted" Book — Don't $hop \`now\` \\path`;

  it.skipIf(skipReal)(
    'm4a: round-trips a torture-value title byte-for-byte',
    async () => {
      const src = await makeFixture('.m4a');
      const result = await tagAudioFileMetadata(src, { title: TORTURE, author: 'Author' });
      expect(result.success).toBe(true);
      expect(result.taggedFilePath).toBeDefined();
      const readBack = await readTag(result.taggedFilePath!, 'title');
      expect(readBack).toBe(TORTURE);
    },
    60_000
  );

  it.skipIf(skipReal)(
    'mp3: round-trips a torture-value title byte-for-byte',
    async () => {
      const src = await makeFixture('.mp3');
      const result = await tagAudioFileMetadata(src, { title: TORTURE, author: 'Author' });
      expect(result.success).toBe(true);
      const readBack = await readTag(result.taggedFilePath!, 'title');
      expect(readBack).toBe(TORTURE);
    },
    60_000
  );

  it.skipIf(skipReal)(
    'flac: round-trips a torture-value title byte-for-byte',
    async () => {
      const src = await makeFixture('.flac');
      const result = await tagAudioFileMetadata(src, { title: TORTURE, author: 'Author' });
      expect(result.success).toBe(true);
      const readBack = await readTag(result.taggedFilePath!, 'title');
      expect(readBack).toBe(TORTURE);
    },
    60_000
  );

  it.skipIf(skipReal)(
    'm4a: author does NOT inherit a stray leading `"` when title contains `"`',
    async () => {
      // The exact #171 symptom: a `"` in one field used to leak as a leading `"`
      // on an adjacent field after shell tokenization. Verify both are clean.
      const src = await makeFixture('.m4a');
      const result = await tagAudioFileMetadata(src, {
        title: 'Has "embedded" quotes',
        author: 'Alexandre Dumas',
      });
      expect(result.success).toBe(true);
      const readAuthor = await readTag(result.taggedFilePath!, 'album_artist');
      expect(readAuthor).toBe('Alexandre Dumas');
      expect(readAuthor?.startsWith('"')).toBe(false);
    },
    60_000
  );
});
