import { execFile } from 'node:child_process';
import { cp, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { promisify } from 'node:util';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';

const execFileAsync = promisify(execFile);
const CLI_BIN = resolve(process.cwd(), 'dist/bin.js');
const EXAMPLE_DIR = resolve(process.cwd(), '../../examples/local-warehouse');

interface CliResult {
  code: number;
  stdout: string;
  stderr: string;
}

interface ExecFailure extends Error {
  code?: number;
  stdout?: string;
  stderr?: string;
}

function isExecFailure(error: unknown): error is ExecFailure {
  return error instanceof Error && ('stdout' in error || 'stderr' in error || 'code' in error);
}

async function runBuiltCli(args: string[]): Promise<CliResult> {
  try {
    const result = await execFileAsync(process.execPath, [CLI_BIN, ...args], {
      encoding: 'utf8',
      timeout: 20_000,
    });
    return {
      code: 0,
      stdout: result.stdout,
      stderr: result.stderr,
    };
  } catch (error) {
    if (!isExecFailure(error)) {
      throw error;
    }
    return {
      code: typeof error.code === 'number' ? error.code : 1,
      stdout: error.stdout ?? '',
      stderr: error.stderr ?? error.message,
    };
  }
}

function parseJsonOutput<T>(stdout: string): T {
  return JSON.parse(stdout) as T;
}

async function copyExampleProject(tempDir: string): Promise<string> {
  const projectDir = join(tempDir, 'local-warehouse');
  await cp(EXAMPLE_DIR, projectDir, { recursive: true });
  return projectDir;
}

describe('standalone local warehouse example', () => {
  let tempDir: string;

  beforeEach(async () => {
    tempDir = await mkdtemp(join(tmpdir(), 'ktx-example-smoke-'));
  });

  afterEach(async () => {
    await rm(tempDir, { recursive: true, force: true });
  });

  it('runs local CLI commands against the copied example project', async () => {
    const projectDir = await copyExampleProject(tempDir);

    const knowledgeList = await runBuiltCli(['wiki', 'revenue', '--json', '--project-dir', projectDir]);
    expect(knowledgeList).toMatchObject({ code: 0, stderr: '' });
    expect(
      parseJsonOutput<{ data: { items: Array<{ key: string; summary: string }> } }>(knowledgeList.stdout).data.items,
    ).toContainEqual(expect.objectContaining({ key: 'revenue', summary: 'Paid order value after refunds' }));

    const slList = await runBuiltCli(['sl', '--json', '--project-dir', projectDir, '--connection-id', 'warehouse']);
    expect(slList).toMatchObject({ code: 0, stderr: '' });
    expect(
      parseJsonOutput<{ data: { items: Array<{ connectionId: string; name: string; columnCount: number }> } }>(
        slList.stdout,
      ).data.items,
    ).toContainEqual(expect.objectContaining({ connectionId: 'warehouse', name: 'orders', columnCount: 3 }));

    const slSearch = await runBuiltCli([
      'sl',
      'search',
      'orders',
      '--json',
      '--connection-id',
      'warehouse',
      '--project-dir',
      projectDir,
    ]);
    expect(slSearch).toMatchObject({ code: 0, stderr: '' });
    expect(
      parseJsonOutput<{ data: { items: Array<{ connectionId: string; name: string }> } }>(slSearch.stdout).data.items,
    ).toContainEqual(expect.objectContaining({ connectionId: 'warehouse', name: 'orders' }));

    const ingest = await runBuiltCli([
      'ingest',
      'run',
      '--connection-id',
      'warehouse',
      '--adapter',
      'fake',
    ]);
    expect(ingest).toMatchObject({ code: 1, stdout: '' });
    expect(ingest.stderr).toContain("unknown option '--adapter'");
  }, 30_000);

});
