import { strict as assert } from 'node:assert';

export interface IRunMxCliResult {
  readonly logs: string[];
  readonly errors: string[];
  readonly exitCode: number;
}

async function loadMxCli(): Promise<{ createProgram: () => import('commander').Command }> {
  const mxCliUrl = new URL('../../src/index.ts', import.meta.url).href;
  return await import(`${mxCliUrl}?test=${Date.now()}-${Math.random().toString(16).slice(2)}`);
}

export async function runMxCli(argv: string[], options: { cwd?: string } = {}): Promise<IRunMxCliResult> {
  const logs: string[] = [];
  const errors: string[] = [];
  let exitCode = 0;

  const originalLog = console.log;
  const originalError = console.error;
  const originalExit = process.exit;
  const originalCwd = process.cwd();
  const { createProgram } = await loadMxCli();

  console.log = (...args: unknown[]) => {
    logs.push(args.map((arg) => String(arg)).join(' '));
  };
  console.error = (...args: unknown[]) => {
    errors.push(args.map((arg) => String(arg)).join(' '));
  };
  (process as unknown as { exit: (code?: number) => never }).exit = ((code?: number) => {
    exitCode = code ?? 0;
    throw new Error(`process.exit(${exitCode})`);
  }) as never;

  if (options.cwd) {
    process.chdir(options.cwd);
  }

  try {
    const program = createProgram();
    await program.parseAsync(argv);
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    if (!message.startsWith('process.exit(')) {
      throw err;
    }
  } finally {
    if (options.cwd) {
      process.chdir(originalCwd);
    }
    console.log = originalLog;
    console.error = originalError;
    (process as unknown as { exit: typeof process.exit }).exit = originalExit;
  }

  return { logs, errors, exitCode };
}

export function parseLastJson(lines: readonly string[]): unknown {
  for (let i = lines.length - 1; i >= 0; i--) {
    const value = lines[i]?.trim() ?? '';
    if (!value) continue;
    try {
      return JSON.parse(value);
    } catch {
      // continue
    }
  }
  throw new Error(`No JSON payload found in output:\n${lines.join('\n')}`);
}

export function assertExitCode(result: IRunMxCliResult, expected: number): void {
  assert.equal(result.exitCode, expected, `Expected exit code ${expected}, got ${result.exitCode}`);
}
