#!/usr/bin/env node

/**
 * Config-Audit Posture Assessment CLI
 * Runs all scanners + scoring in a single Node.js process.
 * Usage: node posture.mjs <target-path> [--json] [--global] [--output-file path]
 * Zero external dependencies.
 */

import { resolve } from 'node:path';
import { writeFile } from 'node:fs/promises';
import { runAllScanners } from './scan-orchestrator.mjs';
import {
  calculateUtilization,
  determineMaturityLevel,
  determineSegment,
  scoreByArea,
  topActions,
  generateScorecard,
  generateHealthScorecard,
} from './lib/scoring.mjs';

/**
 * Run posture assessment and return structured result.
 * @param {string} targetPath
 * @param {object} [opts]
 * @param {boolean} [opts.includeGlobal=false]
 * @param {boolean} [opts.fullMachine=false] - Scan all known locations across the machine
 * @returns {Promise<object>}
 */
export async function runPosture(targetPath, opts = {}) {
  const envelope = await runAllScanners(targetPath, opts);

  // Extract GAP scanner results
  const gapScanner = envelope.scanners.find(s => s.scanner === 'GAP');
  const gapFindings = gapScanner ? gapScanner.findings : [];

  // Calculate scores
  const utilization = calculateUtilization(gapFindings);
  const maturity = determineMaturityLevel(gapFindings, { files: [] });
  const segment = determineSegment(utilization.score);
  const areaScores = scoreByArea(envelope.scanners);
  const actions = topActions(gapFindings);

  return {
    utilization,
    maturity,
    segment,
    areas: areaScores.areas,
    overallGrade: areaScores.overallGrade,
    topActions: actions,
    opportunityCount: gapFindings.length,
    scannerEnvelope: envelope,
  };
}

// --- CLI entry point ---
async function main() {
  const args = process.argv.slice(2);
  let targetPath = '.';
  let outputFile = null;
  let jsonMode = false;
  let rawMode = false;
  let includeGlobal = false;
  let fullMachine = false;

  for (let i = 0; i < args.length; i++) {
    if (args[i] === '--output-file' && args[i + 1]) {
      outputFile = args[++i];
    } else if (args[i] === '--json') {
      jsonMode = true;
    } else if (args[i] === '--raw') {
      rawMode = true;
    } else if (args[i] === '--global') {
      includeGlobal = true;
    } else if (args[i] === '--full-machine') {
      fullMachine = true;
    } else if (args[i] === '--include-fixtures') {
      // handled below
    } else if (!args[i].startsWith('-')) {
      targetPath = args[i];
    }
  }

  const filterFixtures = !args.includes('--include-fixtures');
  const humanizedProgress = !jsonMode && !rawMode;
  const result = await runPosture(targetPath, {
    includeGlobal,
    fullMachine,
    filterFixtures,
    humanizedProgress,
  });

  // stdout JSON path: --json and --raw both write the v5.0.0-shape result
  // (byte-identical). Default mode writes nothing to stdout.
  if (jsonMode || rawMode) {
    const json = JSON.stringify(result, null, 2);
    process.stdout.write(json + '\n');
  }

  // stderr scorecard path: --json suppresses; --raw renders v5.0.0 verbatim
  // (humanized=false); default renders humanized scorecard.
  if (!jsonMode) {
    const scorecard = generateHealthScorecard(
      { areas: result.areas, overallGrade: result.overallGrade },
      result.opportunityCount,
      { humanized: !rawMode },
    );
    process.stderr.write('\n' + scorecard + '\n');
  }

  if (outputFile) {
    const json = JSON.stringify(result, null, 2);
    await writeFile(outputFile, json, 'utf-8');
    process.stderr.write(`\nResults written to ${outputFile}\n`);
  }
}

// Only run CLI if invoked directly
const isDirectRun = process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname);
if (isDirectRun) {
  main().catch(err => {
    process.stderr.write(`Fatal: ${err.message}\n`);
    process.exit(1);
  });
}
