/**
 * reconcile-menu CLI — Entry point.
 *
 * Scans the .smartstack/ba/ tree against downstream documents (use-case.md,
 * screen.md, règles-métier.md, rbac.md) and reconciles them when the menu has
 * been modified after content was created: detects renames (heuristic) and
 * deletions, then rewrites the docs.
 *
 * Usage:
 *   npx tsx skills/business-analyse/reconcile-menu/cli/reconcile-menu/index.ts \
 *     --spec '{"baRoot": ".smartstack/ba"}'
 *
 *   npx tsx skills/business-analyse/reconcile-menu/cli/reconcile-menu/index.ts \
 *     --spec '{"baRoot": ".smartstack/ba", "dryRun": true}'
 */
import { parseArgs } from 'node:util';
import path from 'node:path';
import { generateEnvelope, failGenerate, printEnvelope } from '../../../../lib/output.js';
import { ensureDirectory, writeText } from '../../../../lib/fs.js';
import { validate } from './validate.js';
import { scan } from './scan.js';
import { buildPlan } from './detect.js';
import { applyPlan, buildVerdict } from './clean.js';

const COMMAND = 'reconcile-menu';

async function main(): Promise<void> {
  const { values } = parseArgs({
    options: {
      spec: { type: 'string' },
    },
    strict: false,
  });

  if (!values.spec) {
    printEnvelope(failGenerate(COMMAND, ['Missing required --spec argument']));
    process.exit(1);
  }

  let raw: unknown;
  try {
    raw = JSON.parse(values.spec);
  } catch {
    printEnvelope(failGenerate(COMMAND, ['Invalid JSON in --spec argument']));
    process.exit(1);
  }

  const validation = await validate(raw);
  if (!validation.valid || !validation.spec) {
    printEnvelope(failGenerate(COMMAND, validation.errors));
    process.exit(1);
  }

  const spec = validation.spec;
  const warnings = [...validation.warnings];

  // --- Scan ---
  const scanResult = await scan(spec.baRoot);

  // --- Plan ---
  const plan = buildPlan(scanResult);

  // --- Apply (skip if dry-run) ---
  let filesModified: string[] = [];
  let directoriesRemoved: string[] = [];
  if (!spec.dryRun) {
    const applied = await applyPlan(spec.baRoot, plan.renames, plan.deletions);
    filesModified = applied.filesModified;
    directoriesRemoved = applied.directoriesRemoved;
  }

  // --- Write verdict ---
  const verdict = buildVerdict({
    baRoot: spec.baRoot,
    renames: plan.renames,
    deletions: plan.deletions,
    staleRefsCount: plan.staleRefs.length,
    filesModified,
    directoriesRemoved,
    dryRun: spec.dryRun,
  });

  const verdictPath = path.join(spec.baRoot, '_audit', 'reconcile.md');
  if (!spec.dryRun) {
    await ensureDirectory(path.dirname(verdictPath));
    await writeText(verdictPath, verdict);
    filesModified = Array.from(new Set([...filesModified, verdictPath])).sort();
  }

  // --- Next steps ---
  const nextSteps: string[] = [];
  if (plan.renames.length === 0 && plan.deletions.length === 0) {
    nextSteps.push('Tree consistent with downstream — no action needed');
  } else {
    if (spec.dryRun) {
      nextSteps.push(`Re-run without --dryRun to apply ${plan.renames.length} rename(s) + ${plan.deletions.length} deletion(s)`);
    } else {
      nextSteps.push(`Review _audit/reconcile.md (${plan.renames.length} rename(s) + ${plan.deletions.length} deletion(s))`);
      nextSteps.push('Re-run /ba-loop to re-enrich downstream docs after reconciliation');
    }
  }

  printEnvelope(
    generateEnvelope(COMMAND, {
      data: {
        renames: plan.renames,
        deletions: plan.deletions,
        staleRefs: plan.staleRefs,
        dryRun: spec.dryRun,
        existingSectionsCount: scanResult.existingSections.size,
        referencedSectionsCount: scanResult.referencedSections.size,
        directoriesRemoved,
      },
      filesModified: filesModified.map((f) => path.resolve(f)),
      warnings,
      nextSteps,
    }),
  );
}

main().catch((err) => {
  printEnvelope(
    failGenerate(COMMAND, [err instanceof Error ? err.message : String(err)]),
  );
  process.exit(1);
});
