#!/usr/bin/env node
/**
 * audit-dev-frontend/index.ts — Entry point.
 *
 * parse args → validate → dispatch to audit() or apply() → print envelope → exit code.
 *
 * Exit code: 0 if no `err` findings remain (after apply pass); 1 otherwise.
 *
 * Invoked by Claude (orchestrator subagent) via Bash. The Studio audit-reports
 * UI also reads the `auditReport` field of the envelope to render rule checks.
 */
import { Command } from 'commander';
import { validateArgs } from './validate.js';
import { audit } from './audit.js';
import { apply } from './apply.js';
import type {
  AuditDevFrontendMode,
  AuditDevFrontendOutput,
  Finding,
} from './types.js';

const program = new Command();

program
  .name('audit-dev-frontend')
  .description('Audit / apply UI conformity fixes against the PRD slice on a generated SmartStack web app')
  .requiredOption('--project-path <path>', 'Absolute path to the generated web root (holds src/pages)')
  .requiredOption('--mode <mode>', 'audit | apply', 'audit')
  .requiredOption('--module-code <code>', 'Active module code')
  .requiredOption('--app-code <code>', 'Active application code')
  .option('--prd-slice <path>', 'Path to the PRD frontend slice JSON')
  .option('--inventory <path>', 'Path to a pre-built inventory JSON (otherwise self-built from disk)')
  .option('--rules <list>', 'Comma-separated rule codes (e.g. DEV-UI-001,DEV-UI-005). Default = all')
  .option('--regenerate-threshold <n>', 'Pages with ≥ N errors get rebuilt from template in apply mode (default 3, 0 = disable)', '3')
  .option('--backend-path <path>', 'Path to the .NET project root (for DEV-UI-011 controller scan)')
  .option('--core-navigation-seed-path <path>', 'Path to CoreNavigationSeedDataProvider.cs (for DEV-UI-012)')
  .parse(process.argv);

const opts = program.opts();

const raw = {
  projectPath: opts.projectPath as string,
  mode: opts.mode as AuditDevFrontendMode,
  moduleCode: opts.moduleCode as string,
  appCode: opts.appCode as string,
  prdSlice: opts.prdSlice as string | undefined,
  inventory: opts.inventory as string | undefined,
  rules: typeof opts.rules === 'string'
    ? opts.rules.split(',').map((s: string) => s.trim()).filter(Boolean)
    : [],
  regenerateThreshold: Number(opts.regenerateThreshold ?? 3),
  backendPath: opts.backendPath as string | undefined,
  coreNavigationSeedPath: opts.coreNavigationSeedPath as string | undefined,
};

const validation = validateArgs(raw);
if (!validation.valid || !validation.args) {
  const output: AuditDevFrontendOutput = {
    success: false,
    command: 'audit-dev-frontend',
    data: { mode: (raw.mode ?? 'audit'), rulesRun: [], valid: false },
    report: null,
    auditReport: null,
    filesModified: [],
    errors: validation.errors,
    warnings: [],
    nextSteps: [],
  };
  console.log(JSON.stringify(output, null, 2));
  process.exit(1);
}

try {
  const report = validation.args.mode === 'apply'
    ? await apply(validation.args)
    : await audit(validation.args);

  const valid = report.counts.errors === 0;
  const rulesRun = validation.args.rules.length > 0 ? validation.args.rules : Object.keys(report.byRule);

  // Build the system-prompt-shaped audit report for Studio audit-reports UI.
  const stripped = report.findings.map((f: Finding) => ({
    dimension: f.dimension,
    code: f.code,
    severity: f.severity,
    label: f.label,
    params: f.params,
    solution: f.solution,
    fixSkill: f.fixSkill,
    fixPhaseKey: f.fixPhaseKey,
  }));

  const nextSteps: string[] = [];
  if (report.counts.errors > 0) {
    nextSteps.push(`${report.counts.errors} ERROR finding(s) — blocking frontend audit gate.`);
    if (validation.args.mode === 'audit') {
      const fixable = report.findings.filter((f) => f.autoFixable && f.severity === 'err').length;
      if (fixable > 0) nextSteps.push(`Run with --mode apply to auto-fix ${fixable} mechanical issue(s).`);
    }
  }
  if (validation.args.mode === 'apply' && 'fixesApplied' in report) {
    nextSteps.push(`Applied ${report.fixesApplied} fix(es) across ${report.filesModified.length} file(s). Re-run --mode audit to verify.`);
    if (report.fixesSkipped.length > 0) {
      nextSteps.push(`${report.fixesSkipped.length} finding(s) auto-fixable but could not be rewritten (file parse / ambiguous context). Resolve manually.`);
    }
  }

  const output: AuditDevFrontendOutput = {
    success: true,
    command: 'audit-dev-frontend',
    data: { mode: validation.args.mode, rulesRun, valid },
    report,
    auditReport: {
      scope: 'devFrontend',
      applicationCode: validation.args.appCode,
      moduleCode: validation.args.moduleCode,
      findings: stripped,
    },
    filesModified: 'filesModified' in report ? report.filesModified : [],
    errors: [],
    warnings: [],
    nextSteps,
  };
  console.log(JSON.stringify(output, null, 2));
  process.exit(valid ? 0 : 1);
} catch (err) {
  const message = err instanceof Error ? err.message : String(err);
  const output: AuditDevFrontendOutput = {
    success: false,
    command: 'audit-dev-frontend',
    data: { mode: validation.args.mode, rulesRun: [], valid: false },
    report: null,
    auditReport: null,
    filesModified: [],
    errors: [`Unexpected error: ${message}`],
    warnings: [],
    nextSteps: [],
  };
  console.log(JSON.stringify(output, null, 2));
  process.exit(1);
}
