/**
 * generate.ts — Generate the development plan as a Markdown file.
 *
 * Produces:
 * - Summary table
 * - Wave 0 (external prerequisites) if applicable
 * - Wave 1+ development tables
 * - Mermaid dependency graph
 * - Warnings
 * - Next steps
 */
import type {
  DevelopmentPlan,
  GeneratedFile,
  Wave,
  WaveModule,
  ModuleKey,
} from './types.js';

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

/**
 * Generate the plan markdown from the computed DevelopmentPlan.
 * Returns a single GeneratedFile for `_plan/dev-plan.md`.
 */
export function generatePlan(
  plan: DevelopmentPlan,
  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+ — development waves
  const devWaves = plan.waves.filter((w) => w.level > 0);
  for (const wave of devWaves) {
    lines.push(waveSection(wave, devWaves.length));
    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: DevelopmentPlan): string {
  return `<!-- ba:plan kind=dev-plan generated=${plan.generatedAt} apps=${plan.apps.join(',')} -->`;
}

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

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

  // Count dev-ready modules
  const allDevModules = devWaves.flatMap((w) => w.modules);
  const readyCount = allDevModules.filter(
    (m) => m.readiness?.verdict === 'GO',
  ).length;
  const readyStr = allDevModules.some((m) => m.readiness)
    ? `${readyCount}/${inScopeModules}`
    : 'N/A';

  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 de développement | ${devWaves.length} |`,
    `| Dépendances cross-module | ${plan.totalCrossModuleDeps} |`,
    `| Cycles détectés | ${plan.cycles.length}${plan.cycles.length > 0 ? ' ⚠️' : ''} |`,
    `| Modules dev-ready (GO) | ${readyStr} |`,
  ].join('\n');
}

function externalPrereqs(wave0: Wave): string {
  const lines = [
    '## ⚠️ Wave 0 — Prérequis externes',
    '',
    '> Modules hors scope référencés comme FK cross-module.',
    '> Doivent être développés ou stubbés avant Wave 1.',
    '',
    '| 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: Wave, totalWaves: number): string {
  const subtitle =
    wave.level === 1
      ? ' — Fondations (aucune dépendance intra-scope)'
      : '';

  const lines = [
    `## Wave ${wave.level}${subtitle}`,
    '',
    '| Module | Entités | Dépend de | Audit PRD |',
    '|--------|---------|-----------|-----------|',
  ];

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

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

function formatReadiness(mod: WaveModule): string {
  if (!mod.readiness) return '—';

  switch (mod.readiness.verdict) {
    case 'GO': {
      const score = mod.readiness.score != null ? ` (${mod.readiness.score})` : '';
      return `✅ GO${score}`;
    }
    case 'NO-GO': {
      const score = mod.readiness.score != null ? ` (${mod.readiness.score})` : '';
      const errs =
        mod.readiness.errCount != null ? ` — ${mod.readiness.errCount} err` : '';
      return `❌ NO-GO${score}${errs}`;
    }
    case 'UNKNOWN':
      return '⬜ non audité';
  }
}

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

  // Collect all edges and nodes per wave
  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: dependent → dependency
  // Arrow means "depends on" so A --> B reads "A needs B first"
  const edges = new Set<string>();
  const devWaves = allWaves.filter((w) => w.level > 0);
  for (const wave of devWaves) {
    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);
      }
    }
  }

  // Also 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-développé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: DevelopmentPlan): 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** : développer ou stubber les prérequis externes (${wave0.modules.map((m) => m.key).join(', ')})`,
    );
  }

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

  // Flag NO-GO or unaudited modules
  const allDevModules = devWaves.flatMap((w) => w.modules);
  const noGo = allDevModules.filter(
    (m) => m.readiness?.verdict === 'NO-GO',
  );
  const unaudited = allDevModules.filter(
    (m) => m.readiness?.verdict === 'UNKNOWN',
  );

  if (noGo.length > 0) {
    lines.push(
      `${step++}. ❌ Corriger les erreurs d'audit pour : ${noGo.map((m) => `\`${m.key}\``).join(', ')} avant \`/ba-develop\``,
    );
  }

  if (unaudited.length > 0) {
    lines.push(
      `${step++}. ⬜ Lancer \`/ba-create-prd\` + \`/ba-audit-prd\` pour : ${unaudited.map((m) => `\`${m.key}\``).join(', ')}`,
    );
  }

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