#!/usr/bin/env node

/**
 * Config-Audit Self-Audit
 * Runs the plugin's own scanners on its own configuration.
 * CLI: node self-audit.mjs [--json] [--fix]
 * Exit codes: 0=PASS (no critical/high), 1=WARN (high findings), 2=FAIL (critical findings)
 * Zero external dependencies.
 */

import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { runAllScanners } from './scan-orchestrator.mjs';
import { scan as scanPluginHealth } from './plugin-health-scanner.mjs';
import { scoreByArea } from './lib/scoring.mjs';
import { gradeFromPassRate } from './lib/severity.mjs';
import { loadSuppressions, applySuppressions } from './lib/suppression.mjs';

const __dirname = dirname(fileURLToPath(import.meta.url));
const PLUGIN_ROOT = resolve(__dirname, '..');

/**
 * Run self-audit on this plugin.
 * @param {object} [opts]
 * @param {boolean} [opts.fix=false] - Run fix-engine on auto-fixable findings
 * @returns {Promise<object>} Combined result
 */
export async function runSelfAudit(opts = {}) {
  const pluginDir = PLUGIN_ROOT;

  // 1. Run all config scanners on plugin root
  // Fixture filtering is handled automatically by runAllScanners (filterFixtures defaults to true)
  const configEnvelope = await runAllScanners(pluginDir);

  // 2. Run plugin health scanner + apply suppressions
  const pluginHealthResult = await scanPluginHealth(pluginDir);
  const { suppressions } = await loadSuppressions(pluginDir);
  if (suppressions.length > 0) {
    const { active, suppressed } = applySuppressions(pluginHealthResult.findings, suppressions);
    pluginHealthResult.findings = active;
    pluginHealthResult.suppressedFindings = suppressed;
  }

  // 3. Score config quality
  const areaScores = scoreByArea(configEnvelope.scanners);
  const avgScore = areaScores.areas.length > 0
    ? Math.round(areaScores.areas.reduce((s, a) => s + a.score, 0) / areaScores.areas.length)
    : 0;
  const configGrade = gradeFromPassRate(avgScore);

  // 4. Score plugin health
  const pluginIssueCount = pluginHealthResult.findings.length;
  const pluginScore = Math.max(0, 100 - pluginIssueCount * 10);
  const pluginGrade = gradeFromPassRate(pluginScore);

  // 5. Determine overall result
  const allFindings = [
    ...configEnvelope.scanners.flatMap(s => s.findings),
    ...pluginHealthResult.findings,
  ];

  const hasCritical = allFindings.some(f => f.severity === 'critical');
  const hasHigh = allFindings.some(f => f.severity === 'high');
  let exitCode = 0;
  let verdict = 'PASS';
  if (hasCritical) { exitCode = 2; verdict = 'FAIL'; }
  else if (hasHigh) { exitCode = 1; verdict = 'WARN'; }

  // 6. Optionally fix
  let fixResult = null;
  if (opts.fix && allFindings.some(f => f.autoFixable)) {
    try {
      const { planFixes, applyFixes } = await import('./fix-engine.mjs');
      const plan = planFixes(configEnvelope);
      if (plan.length > 0) {
        fixResult = await applyFixes(plan);
      }
    } catch {
      // Fix engine unavailable or failed — non-fatal
    }
  }

  return {
    pluginDir,
    configGrade,
    configScore: avgScore,
    pluginGrade,
    pluginScore,
    configEnvelope,
    pluginHealthResult,
    allFindings,
    exitCode,
    verdict,
    fixResult,
  };
}

/**
 * Format self-audit result for terminal display.
 * @param {object} result - From runSelfAudit()
 * @returns {string}
 */
export function formatSelfAudit(result) {
  const lines = [];
  lines.push('\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501');
  lines.push(' Config-Audit Self-Audit');
  lines.push('\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501');
  lines.push('');
  lines.push(` Plugin health: ${result.pluginGrade} (${result.pluginScore})`);
  lines.push(` Config quality: ${result.configGrade} (${result.configScore})`);
  lines.push('');

  // Issues summary
  const nonInfo = result.allFindings.filter(f => f.severity !== 'info');
  if (nonInfo.length > 0) {
    lines.push(` Issues (${nonInfo.length}):`);
    for (const f of nonInfo.slice(0, 10)) {
      lines.push(`  - [${f.severity}] ${f.title}`);
    }
    if (nonInfo.length > 10) {
      lines.push(`  ...and ${nonInfo.length - 10} more`);
    }
  } else {
    lines.push(' Issues (0)');
  }

  lines.push('');

  // Fix results
  if (result.fixResult) {
    const applied = result.fixResult.filter(r => r.status === 'applied').length;
    lines.push(` Auto-fix: ${applied} fix(es) applied`);
    lines.push('');
  }

  // Verdict
  if (result.verdict === 'PASS') {
    lines.push(' Self-audit: PASS');
    lines.push(' (No critical or high findings)');
  } else if (result.verdict === 'WARN') {
    lines.push(' Self-audit: WARN');
    lines.push(' (High-severity findings detected)');
  } else {
    lines.push(' Self-audit: FAIL');
    lines.push(' (Critical findings detected)');
  }

  lines.push('');
  lines.push('\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501');

  return lines.join('\n');
}

// --- CLI entry point ---
async function main() {
  const args = process.argv.slice(2);
  const jsonMode = args.includes('--json');
  const fixMode = args.includes('--fix');

  const result = await runSelfAudit({ fix: fixMode });

  if (jsonMode) {
    const json = JSON.stringify(result, null, 2) + '\n';
    await new Promise(resolve => process.stdout.write(json, resolve));
  } else {
    process.stderr.write('\n' + formatSelfAudit(result) + '\n');
  }

  process.exitCode = result.exitCode;
}

const isDirectRun = process.argv[1] && resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));
if (isDirectRun) {
  main().catch(err => {
    process.stderr.write(`Fatal: ${err.message}\n`);
    process.exit(3);
  });
}
