import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';

const defaultThresholds = {
  errors: 0,
  errorRate: 0.001,
  p95Ms: 300,
  p99Ms: 800,
  concurrency: 30,
  durationSeconds: 120,
};

function parseArgs(argv) {
  const options = {
    input: '',
    output: '',
    json: false,
    quiet: false,
    allowWrites: false,
    thresholds: { ...defaultThresholds },
  };
  for (let index = 2; index < argv.length; index += 1) {
    const arg = argv[index];
    if (arg === '--json') options.json = true;
    else if (arg === '--quiet') options.quiet = true;
    else if (arg === '--allow-writes') options.allowWrites = true;
    else if (arg === '--input') {
      options.input = argv[index + 1] || '';
      index += 1;
    } else if (arg.startsWith('--input=')) {
      options.input = arg.slice('--input='.length);
    } else if (arg === '--output') {
      options.output = argv[index + 1] || '';
      index += 1;
    } else if (arg.startsWith('--output=')) {
      options.output = arg.slice('--output='.length);
    } else if (arg === '--max-p95-ms') {
      options.thresholds.p95Ms = numberArg(argv[index + 1], options.thresholds.p95Ms);
      index += 1;
    } else if (arg.startsWith('--max-p95-ms=')) {
      options.thresholds.p95Ms = numberArg(arg.slice('--max-p95-ms='.length), options.thresholds.p95Ms);
    } else if (arg === '--max-p99-ms') {
      options.thresholds.p99Ms = numberArg(argv[index + 1], options.thresholds.p99Ms);
      index += 1;
    } else if (arg.startsWith('--max-p99-ms=')) {
      options.thresholds.p99Ms = numberArg(arg.slice('--max-p99-ms='.length), options.thresholds.p99Ms);
    } else if (arg === '--max-error-rate') {
      options.thresholds.errorRate = numberArg(argv[index + 1], options.thresholds.errorRate);
      index += 1;
    } else if (arg.startsWith('--max-error-rate=')) {
      options.thresholds.errorRate = numberArg(arg.slice('--max-error-rate='.length), options.thresholds.errorRate);
    } else if (arg === '--min-concurrency') {
      options.thresholds.concurrency = numberArg(argv[index + 1], options.thresholds.concurrency);
      index += 1;
    } else if (arg.startsWith('--min-concurrency=')) {
      options.thresholds.concurrency = numberArg(arg.slice('--min-concurrency='.length), options.thresholds.concurrency);
    } else if (arg === '--min-duration-seconds') {
      options.thresholds.durationSeconds = numberArg(argv[index + 1], options.thresholds.durationSeconds);
      index += 1;
    } else if (arg.startsWith('--min-duration-seconds=')) {
      options.thresholds.durationSeconds = numberArg(arg.slice('--min-duration-seconds='.length), options.thresholds.durationSeconds);
    } else if (!arg.startsWith('-') && !options.input) {
      options.input = arg;
    }
  }
  return options;
}

function numberArg(value, fallback) {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : fallback;
}

function readJson(filePath) {
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
}

function numberValue(value, fallback = 0) {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : fallback;
}

function booleanValue(value) {
  return value === true || value === 'true';
}

function summarizeBenchmark(report) {
  const summary = report.summary || {};
  const latencyOk = summary.latencyOk || {};
  const config = report.config || {};
  return {
    errors: numberValue(summary.errors),
    errorRate: numberValue(summary.errorRate),
    p95Ms: numberValue(latencyOk.p95Ms),
    p99Ms: numberValue(latencyOk.p99Ms),
    concurrency: numberValue(config.concurrency),
    durationSeconds: numberValue(config.durationSeconds),
    includeWrites: booleanValue(config.includeWrites),
    requests: numberValue(summary.requests),
    ok: numberValue(summary.ok),
    throughputRps: numberValue(summary.throughputRps),
    apiBase: report.apiBase || '',
    startedAt: report.startedAt || '',
    finishedAt: report.finishedAt || '',
  };
}

function evaluateSummary(summary, thresholds = defaultThresholds, options = {}) {
  const failures = [];
  if (summary.errors !== thresholds.errors) failures.push(`errors expected ${thresholds.errors} but got ${summary.errors}`);
  if (summary.errorRate > thresholds.errorRate) failures.push(`errorRate expected <= ${thresholds.errorRate} but got ${summary.errorRate}`);
  if (summary.p95Ms > thresholds.p95Ms) failures.push(`p95Ms expected <= ${thresholds.p95Ms} but got ${summary.p95Ms}`);
  if (summary.p99Ms > thresholds.p99Ms) failures.push(`p99Ms expected <= ${thresholds.p99Ms} but got ${summary.p99Ms}`);
  if (summary.concurrency < thresholds.concurrency) failures.push(`concurrency expected >= ${thresholds.concurrency} but got ${summary.concurrency}`);
  if (summary.durationSeconds < thresholds.durationSeconds) failures.push(`durationSeconds expected >= ${thresholds.durationSeconds} but got ${summary.durationSeconds}`);
  if (!options.allowWrites && summary.includeWrites !== false) failures.push('includeWrites expected false');
  return {
    status: failures.length ? 'fail' : 'pass',
    failures,
    summary,
    thresholds,
    options: {
      allowWrites: Boolean(options.allowWrites),
    },
  };
}

function launchGateCheck(evaluation) {
  return {
    id: 'performance.api-real-data-read',
    status: evaluation.status,
    command: 'npm run perf:api:local',
    completedAt: new Date().toISOString(),
    artifact: '',
    summary: {
      errors: evaluation.summary.errors,
      errorRate: evaluation.summary.errorRate,
      p95Ms: evaluation.summary.p95Ms,
      p99Ms: evaluation.summary.p99Ms,
      concurrency: evaluation.summary.concurrency,
      durationSeconds: evaluation.summary.durationSeconds,
      includeWrites: evaluation.summary.includeWrites,
    },
  };
}

function capacityObservation(evaluation) {
  return {
    id: evaluation.summary.includeWrites ? 'performance.api-real-data-mixed' : 'performance.api-real-data-read-observation',
    status: evaluation.status,
    command: 'npm run perf:api:local',
    completedAt: new Date().toISOString(),
    artifact: '',
    summary: {
      errors: evaluation.summary.errors,
      errorRate: evaluation.summary.errorRate,
      p95Ms: evaluation.summary.p95Ms,
      p99Ms: evaluation.summary.p99Ms,
      concurrency: evaluation.summary.concurrency,
      durationSeconds: evaluation.summary.durationSeconds,
      includeWrites: evaluation.summary.includeWrites,
      requests: evaluation.summary.requests,
      throughputRps: evaluation.summary.throughputRps,
    },
  };
}

async function main() {
  const options = parseArgs(process.argv);
  if (!options.input) {
    console.error('Usage: node scripts/performance-summary.js --input docs/refactor/performance-reports/api-benchmark-xxx.json [--json]');
    process.exitCode = 1;
    return;
  }

  const inputPath = path.resolve(process.cwd(), options.input);
  const report = readJson(inputPath);
  const summary = summarizeBenchmark(report);
  const evaluation = evaluateSummary(summary, options.thresholds, { allowWrites: options.allowWrites });
  const evidenceKey = options.allowWrites ? 'capacityObservation' : 'launchGateCheck';
  const evidenceValue = options.allowWrites ? capacityObservation(evaluation) : launchGateCheck(evaluation);
  const payload = {
    input: inputPath,
    evaluation,
    [evidenceKey]: {
      ...evidenceValue,
      artifact: options.input,
    },
  };

  if (options.output) {
    const outputPath = path.resolve(process.cwd(), options.output);
    fs.mkdirSync(path.dirname(outputPath), { recursive: true });
    fs.writeFileSync(outputPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
  }

  if (options.json) {
    console.log(JSON.stringify(payload, null, 2));
  } else if (!options.quiet) {
    console.log(`[${evaluation.status.toUpperCase()}] ${path.basename(inputPath)} p95=${summary.p95Ms}ms p99=${summary.p99Ms}ms errors=${summary.errors} errorRate=${summary.errorRate} concurrency=${summary.concurrency} duration=${summary.durationSeconds}s`);
    for (const failure of evaluation.failures) console.log(`- ${failure}`);
  }

  if (evaluation.status !== 'pass') process.exitCode = 1;
}

if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
  main();
}

export { capacityObservation, defaultThresholds, evaluateSummary, launchGateCheck, summarizeBenchmark };
