/**
 * generate.ts — Generate the BA order plan as Markdown + JSON.
 *
 * Produces:
 * - Summary table
 * - Wave 0 (external prerequisites) if applicable
 * - Wave 1+ BA order tables
 * - Mermaid dependency graph
 * - Warnings
 * - Next steps (which modules to analyze first)
 */
import type {
  BaOrderPlan,
  BaWave,
  BaWaveModule,
  GeneratedFile,
  ModuleKey,
} from './types.js';

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

/**
 * Generate the BA order plan markdown.
 */
export function generateBaOrder(
  plan: BaOrderPlan,
  outputPath: string,
): GeneratedFile {
  const lines: string[] = [];

  lines.push(anchor(plan));
  lines.push('');
  lines.push(heading(plan));
  lines.push('');
  lines.push(summary(plan));
  lines.push('');

  // Wave 0 — external prerequisites
  const wave0 = plan.waves.find((w) => w.level === 0);
  if (wave0 && wave0.modules.length > 0) {
    lines.push(externalPrereqs(wave0));
    lines.push('');
  }

  // Wave 1+ — BA order waves
  const baWaves = plan.waves.filter((w) => w.level > 0);
  for (const wave of baWaves) {
    lines.push(waveSection(wave));
    lines.push('');
  }

  // Mermaid graph
  lines.push(mermaidGraph(plan));
  lines.push('');

  // Cycles
  if (plan.cycles.length > 0) {
    lines.push(cyclesSection(plan.cycles));
    lines.push('');
  }

  // Warnings
  if (plan.warnings.length > 0) {
    lines.push(warningsSection(plan.warnings));
    lines.push('');
  }

  // Next steps
  lines.push(nextSteps(plan));
  lines.push('');

  return { path: outputPath, content: lines.join('\n') };
}

// ---------------------------------------------------------------------------
// Section generators
// ---------------------------------------------------------------------------

function anchor(plan: BaOrderPlan): string {
  return `<!-- ba:plan kind=ba-order generated=${plan.generatedAt} apps=${plan.apps.join(',')} source=${plan.source} -->`;
}

function heading(plan: BaOrderPlan): string {
  const baWaves = plan.waves.filter((w) => w.level > 0);
  const inScopeModules = baWaves.reduce((sum, w) => sum + w.modules.length, 0);
  return [
    `# Ordre d'analyse BA — ${plan.apps.join(', ')}`,
    '',
    `_Généré le ${plan.generatedAt} · ${inScopeModules} modules · ${baWaves.length} vagues_`,
  ].join('\n');
}

function summary(plan: BaOrderPlan): string {
  const wave0 = plan.waves.find((w) => w.level === 0);
  const baWaves = plan.waves.filter((w) => w.level > 0);
  const externalCount = wave0?.modules.length ?? 0;
  const inScopeModules = baWaves.reduce((sum, w) => sum + w.modules.length, 0);

  return [
    '## Résumé',
    '',
    '| Métrique | Valeur |',
    '|----------|--------|',
    `| Applications | ${plan.apps.length} (${plan.apps.join(', ')}) |`,
    `| Modules dans le scope | ${inScopeModules} |`,
    `| Prérequis externes | ${externalCount}${externalCount > 0 ? ' ⚠️' : ''} |`,
    `| Vagues BA | ${baWaves.length} |`,
    `| Dépendances inter-modules | ${plan.totalDeps} |`,
    `| Cycles détectés | ${plan.cycles.length}${plan.cycles.length > 0 ? ' ⚠️' : ''} |`,
    `| Source | Contexte menu (pré-data model) |`,
  ].join('\n');
}

function externalPrereqs(wave0: BaWave): string {
  const lines = [
    '## ⚠️ Wave 0 — Prérequis externes',
    '',
    '> Modules hors scope référencés comme dépendances.',
    '> Leur analyse BA doit être complétée en amont.',
    '',
    '| Module | Requis par |',
    '|--------|------------|',
  ];

  for (const mod of wave0.modules) {
    const refs = mod.referencedBy?.join(', ') ?? '—';
    lines.push(`| ${mod.key} | ${refs} |`);
  }

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

function waveSection(wave: BaWave): string {
  const subtitle =
    wave.level === 1
      ? ' — Fondations (aucune dépendance intra-scope)'
      : '';

  const lines = [
    `## Wave ${wave.level}${subtitle}`,
    '',
    '> Compléter **toutes les phases BA** (acteurs → use cases → règles → RBAC → data model → écrans) pour ces modules avant de passer à la vague suivante.',
    '',
    '| Module | Dépend de |',
    '|--------|-----------|',
  ];

  for (const mod of wave.modules) {
    const deps = mod.dependsOn.length > 0 ? mod.dependsOn.join(', ') : '—';
    lines.push(`| ${mod.key} | ${deps} |`);
  }

  if (wave.modules.length > 1) {
    lines.push('');
    lines.push(`_${wave.modules.length} modules dans cette vague — parallélisables._`);
  }

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

function mermaidGraph(plan: BaOrderPlan): string {
  const lines = ['## Graphe de dépendances', '', '```mermaid', 'graph LR'];

  const allWaves = plan.waves;
  for (const wave of allWaves) {
    const label =
      wave.level === 0
        ? 'Wave 0 — External'
        : `Wave ${wave.level}`;
    lines.push(`  subgraph "${label}"`);

    for (const mod of wave.modules) {
      const nodeId = mermaidNodeId(mod.key);
      const prefix = mod.isExternal ? '⚠️ ' : '';
      lines.push(`    ${nodeId}["${prefix}${mod.key}"]`);
    }

    lines.push('  end');
  }

  // Collect edges: dependency → dependent (arrow means "must be done before")
  const edges = new Set<string>();
  const baWaves = allWaves.filter((w) => w.level > 0);
  for (const wave of baWaves) {
    for (const mod of wave.modules) {
      for (const dep of mod.dependsOn) {
        const fromId = mermaidNodeId(dep);
        const toId = mermaidNodeId(mod.key);
        const edge = `  ${fromId} --> ${toId}`;
        edges.add(edge);
      }
    }
  }

  // Add edges from externals to their dependents
  const wave0 = allWaves.find((w) => w.level === 0);
  if (wave0) {
    for (const ext of wave0.modules) {
      for (const ref of ext.referencedBy ?? []) {
        const fromId = mermaidNodeId(ext.key);
        const toId = mermaidNodeId(ref);
        const edge = `  ${fromId} --> ${toId}`;
        edges.add(edge);
      }
    }
  }

  for (const edge of edges) {
    lines.push(edge);
  }

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

/** Convert MODULE_KEY (APP/MODULE) to a valid Mermaid node ID. */
function mermaidNodeId(key: ModuleKey | string): string {
  return key.replace(/\//g, '_').replace(/-/g, '_');
}

function cyclesSection(cycles: ModuleKey[][]): string {
  const lines = [
    '## ⚠️ Dépendances cycliques',
    '',
    '> Les modules dans un cycle ont des dépendances mutuelles.',
    '> Ils sont placés dans la même vague et doivent être co-analysés.',
    '',
  ];

  for (let i = 0; i < cycles.length; i++) {
    lines.push(`${i + 1}. ${cycles[i].join(' ↔ ')}`);
  }

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

function warningsSection(warnings: string[]): string {
  const lines = ['## ⚠️ Avertissements', ''];
  for (const w of warnings) {
    lines.push(`- ${w}`);
  }
  return lines.join('\n');
}

function nextSteps(plan: BaOrderPlan): string {
  const lines = ['## Prochaines étapes', ''];
  let step = 1;

  const wave0 = plan.waves.find((w) => w.level === 0);
  if (wave0 && wave0.modules.length > 0) {
    lines.push(
      `${step++}. **Wave 0** : compléter l'analyse BA des prérequis externes (${wave0.modules.map((m) => m.key).join(', ')})`,
    );
  }

  const baWaves = plan.waves.filter((w) => w.level > 0);
  for (const wave of baWaves) {
    const moduleList = wave.modules.map((m) => `\`${m.key}\``).join(', ');
    const parallel = wave.modules.length > 1 ? ' (parallélisable)' : '';
    lines.push(
      `${step++}. **Wave ${wave.level}** : phases 2-7 pour ${moduleList}${parallel}`,
    );
  }

  lines.push('');
  lines.push(
    '> Pour chaque module : `/ba-create-actors` → `/ba-create-use-case` → `/ba-create-business-rules` → `/ba-create-rbac` → `/ba-create-data-model` → `/ba-create-screen`',
  );

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