import test from 'node:test';
import assert from 'node:assert/strict';
import { existsSync, readFileSync, mkdtempSync, writeFileSync, rmSync, linkSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { join, resolve } from 'node:path';
import { tmpdir } from 'node:os';

const root = process.env.CLI_TEST_ROOT ? resolve(process.env.CLI_TEST_ROOT) : process.cwd();
const cli = join(root, 'dist', 'cli.mjs');
const ready = existsSync(cli) && existsSync(join(root, 'examples', 'document-quotation.json'));

test('every documented CLI command works with the source kit alone', { skip: !ready, timeout: 180_000 }, () => {
  const temporary = mkdtempSync(join(tmpdir(), 'essential-tools-cli-'));
  const run = (args: string[], input?: string) => spawnSync(process.execPath, [cli, ...args], { cwd: root, input, encoding: 'utf8', timeout: 60_000, maxBuffer: 4_000_000, shell: false });
  try {
    assert.equal(run(['--help']).status, 0);
    assert.equal(JSON.parse(run(['capabilities']).stdout).mcp, false);
    for (const [kind, filename] of [['document', 'document-quotation'], ['plan', 'plan-customer-portal'], ['process', 'process-onboarding']]) {
      const result = run(['validate', kind, '--input', `examples/${filename}.json`]);
      assert.equal(result.status, 0, result.stderr); assert.equal(JSON.parse(result.stdout).ok, true);
    }
    const originalPlan = readFileSync(join(root, 'examples', 'plan-customer-portal.json'), 'utf8');
    const stdin = run(['plan', 'calculate', '--input', '-'], originalPlan);
    assert.equal(stdin.status, 0, stdin.stderr);
    assert.equal(JSON.parse(stdin.stdout).totalCost, '37720.00');
    const commands = [
      ['document', 'pdf', '--input', 'examples/document-quotation.json', '--output', join(temporary, 'quote.pdf')],
      ...['json', 'csv', 'pdf'].map(format => ['plan', 'export', '--input', 'examples/plan-customer-portal.json', '--format', format, '--output', join(temporary, `plan.${format}`)]),
      ...['mermaid', 'json', 'svg', 'png', 'pdf'].map(format => ['process', 'render', '--input', 'examples/process-onboarding.json', '--format', format, '--output', join(temporary, `process.${format}`)]),
    ];
    for (const args of commands) {
      const result = run(args);
      assert.equal(result.status, 0, `${args.join(' ')}\n${result.stderr}`);
      const success = JSON.parse(result.stdout);
      const bytes = readFileSync(success.output);
      assert.ok(bytes.length > 20); assert.equal(success.bytes, bytes.length);
      if (success.format === 'pdf') assert.equal(bytes.subarray(0, 5).toString(), '%PDF-');
      if (success.format === 'png') assert.equal(bytes.subarray(1, 4).toString(), 'PNG');
      if (success.format === 'svg') { assert.ok(bytes.toString().includes('<svg')); assert.ok(!bytes.toString().includes('<foreignObject')); }
    }
    const existing = commands[1];
    const original = readFileSync(existing.at(-1)!);
    assert.equal(run(existing).status, 2);
    assert.deepEqual(readFileSync(existing.at(-1)!), original);
    assert.equal(run([...existing, '--force']).status, 0);
    assert.equal(run(['validate', 'plan', '--input', '-'], '{"schemaVersion":99}').status, 2);
    assert.equal(run(['validate', 'plan', '--input', '-'], '{"__proto__":{"polluted":true}}').status, 2);
    assert.equal(run(['validate', 'plan', '--input', '-'], 'not JSON').status, 2);
    const future = join(temporary, 'future.json'); writeFileSync(future, JSON.stringify({ schemaVersion: 2 }));
    const missing = run(['plan', 'export', '--input', future, '--format', 'pdf', '--output', join(temporary, 'invalid.pdf')]);
    assert.equal(missing.status, 2); assert.equal(existsSync(join(temporary, 'invalid.pdf')), false);
    assert.equal(run(['plan', 'export', '--input', 'examples/plan-customer-portal.json', '--format', 'json', '--output', 'examples/plan-customer-portal.json', '--force']).status, 2);
    const inputFile = join(temporary, 'CASE-PROBE.json');
    writeFileSync(inputFile, originalPlan);
    const hardLink = join(temporary, 'hard-link.json');
    linkSync(inputFile, hardLink);
    const aliases = [hardLink, ...(process.platform === 'win32' ? [join(temporary, 'case-probe.json')] : [])];
    for (const alias of aliases) {
      const result = run(['plan', 'export', '--input', inputFile, '--format', 'csv', '--output', alias, '--force']);
      assert.equal(result.status, 2, result.stderr);
      assert.match(JSON.parse(result.stderr).error.message, /input file will not be overwritten/);
      assert.equal(readFileSync(inputFile, 'utf8'), originalPlan);
      assert.equal(readFileSync(alias, 'utf8'), originalPlan);
    }
  } finally {
    if (!temporary.startsWith(join(tmpdir(), 'essential-tools-cli-'))) throw new Error('Unexpected test output path.');
    rmSync(temporary, { recursive: true, force: true });
  }
});
