/**
 * Generates `cliter/business-rules-index.json` and `docs/business-rules/INDEX.md`
 * by walking the catalog. Fails on duplicate identifiers, malformed frontmatter,
 * or rules without a parseable metadata table.
 *
 * The index-building and human-index logic lives in the shared catalog runtime
 * (`../_catalog-runtime/build-index`): this script shrinks to BR-specific
 * config — the field reader, the (constant) inclusion predicate, and the file
 * writes — plus the shared `runCatalogMain` guard.
 *
 * Invoked via `pnpm br:generate`.
 */
import { writeFileSync, mkdirSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { parseCatalog, findRepoRoot } from './parse-catalog';
import {
  buildIndex,
  buildHumanIndex,
  runCatalogMain,
  TOTAL_FILES_MODE,
} from '../_catalog-runtime';
import type { ReadFieldsResult } from '../_catalog-runtime';
import type { FileFrontmatter } from './types';

/**
 * Reads the BR index fields from the already-typed `FileFrontmatter`. BR
 * requires `aggregates`; both `bounded_contexts` and `aggregates` must be
 * arrays. On failure the offending field is named so `buildIndex` reports it
 * precisely instead of a blanket "Frontmatter inválido".
 */
function brReadFields(frontmatter: FileFrontmatter): ReadFieldsResult {
  if (!frontmatter || !Array.isArray(frontmatter.bounded_contexts)) {
    return { ok: false, field: 'bounded_contexts' };
  }
  if (!Array.isArray(frontmatter.aggregates)) {
    return { ok: false, field: 'aggregates' };
  }
  return {
    ok: true,
    fields: {
      bounded_contexts: frontmatter.bounded_contexts,
      aggregates: frontmatter.aggregates,
      paths: frontmatter.paths,
      keywords: frontmatter.keywords,
      last_updated: frontmatter.last_updated,
    },
  };
}

function ensureDir(filePath: string): void {
  mkdirSync(dirname(filePath), { recursive: true });
}

async function main(): Promise<void> {
  const repoRoot = findRepoRoot();
  const parsed = await parseCatalog({ repoRoot });
  const index = buildIndex<FileFrontmatter>({
    parsed,
    readFields: brReadFields,
    // BR has no scope-based portability filter: every parsed file participates.
    isFileInstalled: () => true,
    totalFilesMode: TOTAL_FILES_MODE.ALL_PARSED,
  });

  const indexJsonPath = resolve(repoRoot, 'cliter/business-rules-index.json');
  ensureDir(indexJsonPath);
  writeFileSync(indexJsonPath, JSON.stringify(index, null, 2) + '\n', 'utf-8');

  const indexMdPath = resolve(repoRoot, 'docs/business-rules/INDEX.md');
  ensureDir(indexMdPath);
  writeFileSync(
    indexMdPath,
    buildHumanIndex(index, { title: 'Business Rules — Index', generateCmd: 'br:generate' }),
    'utf-8',
  );

  console.log(
    `✓ Índice regenerado: ${index.totalRules} reglas (${index.activeRules} activas, ${index.derogatedRules} derogadas, ${index.proposedRules} propuestas) en ${index.totalFiles} archivos.`,
  );
  console.log(`  → ${indexJsonPath}`);
  console.log(`  → ${indexMdPath}`);
}

runCatalogMain(main, 'generate-index');
