/**
 * Bootstrap helper for retroactively documenting rules implicit in code.
 *
 * This script is intentionally thin: the semantic analysis (which rules exist
 * implicitly in the code, how to phrase them, what the exhaustive case table
 * looks like) is done by the LLM that invokes this command inside Claude Code.
 * The script's job is to:
 *
 *   1. Validate the aggregate exists in `cliter/<bc>/<aggregate>.aurora.yaml`.
 *   2. List the source files the LLM should consult.
 *   3. Create the catalog file with frontmatter scaffold if missing.
 *   4. Print guidance for the LLM to follow.
 *
 * Usage:
 *   The script is invoked from the slash command /business-rules:document
 *   (it is NOT exposed as `pnpm br:*` because the dev's flow always goes
 *   through Claude Code: the script creates the scaffold and lists sources;
 *   Claude continues the session reading those sources to propose the
 *   actual rules).
 *
 *   Direct invocation (e.g. for tooling or debugging):
 *     npx tsx scripts/business-rules/document.ts <bc>/<aggregate>
 *     npx tsx scripts/business-rules/document.ts <bc>/<aggregate> --from <path-to-md>
 */
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import fastGlob from 'fast-glob';
import { findRepoRoot } from './parse-catalog';

interface Args {
  boundedContext: string;
  aggregate: string;
  fromMd?: string;
}

function parseArgs(): Args {
  const args = process.argv.slice(2);
  const positional = args.filter((a) => !a.startsWith('--'));
  if (positional.length === 0) {
    console.error(
      'Uso: npx tsx scripts/business-rules/document.ts <bc>/<aggregate> [--from <path-to-md>]',
    );
    console.error(
      '     (Normalmente invocado vía /business-rules:document desde Claude Code.)',
    );
    process.exit(1);
  }
  const slug = positional[0];
  const slashIdx = slug.indexOf('/');
  if (slashIdx === -1) {
    console.error(
      `✗ Formato inválido. Usa <bc>/<aggregate>, p. ej. production-planning/production-order-position-event`,
    );
    process.exit(1);
  }
  const fromIdx = args.indexOf('--from');
  const fromMd = fromIdx >= 0 ? args[fromIdx + 1] : undefined;
  return {
    boundedContext: slug.slice(0, slashIdx),
    aggregate: slug.slice(slashIdx + 1),
    fromMd,
  };
}

async function listSourcePaths(
  repoRoot: string,
  bc: string,
  aggregate: string,
): Promise<string[]> {
  const patterns = [
    `cliter/${bc}/CONTEXT.md`,
    `cliter/${bc}/${aggregate}.aurora.yaml`,
    `backend/src/@app/${bc}/${aggregate}/**/*.ts`,
    `backend/src/@api/${bc}/${aggregate}/**/*.ts`,
    `frontend/src/app/modules/admin/apps/${bc}/${aggregate}/**/*.{ts,html}`,
  ];
  const found = await fastGlob(patterns, {
    cwd: repoRoot,
    ignore: ['**/node_modules/**', '**/dist/**', '**/.angular/**', '**/*.spec.ts'],
  });
  return found.sort();
}

async function listTestPaths(
  repoRoot: string,
  bc: string,
  aggregate: string,
): Promise<string[]> {
  const patterns = [
    `backend/src/@app/${bc}/${aggregate}/**/*.spec.ts`,
    `backend/src/@api/${bc}/${aggregate}/**/*.spec.ts`,
    `backend/test/**/${aggregate}/**/*.spec.ts`,
    `frontend/src/app/modules/admin/apps/${bc}/${aggregate}/**/*.spec.ts`,
  ];
  const found = await fastGlob(patterns, {
    cwd: repoRoot,
    ignore: ['**/node_modules/**', '**/dist/**'],
  });
  return found.sort();
}

function buildSkeleton(bc: string, aggregate: string, today: string): string {
  return `---
bounded_contexts:
  - ${bc}
aggregates:
  - ${bc}/${aggregate}
last_updated: ${today}
keywords:
  - ${aggregate.replace(/-/g, ' ')}
  # añadir keywords bilingües (español + inglés)
paths:
  - backend/src/@app/${bc}/${aggregate}/**
  - backend/src/@api/${bc}/${aggregate}/**
  - frontend/src/app/modules/admin/apps/${bc}/${aggregate}/**
---

# Reglas de ${humanize(aggregate)}

> **Glosario**:
>
> - **<concepto>**: <breve definición>

---

<!--
  Documenta aquí las reglas siguiendo la plantilla de \`docs/business-rules/README.md\`:
  - validation
  - cascade (con diagrama)
  - derived
  - policy

  Cada regla:
    ## BR-<BC>-<AGG>-<NNN> — <título>
    | Tipo / Estado / Severidad / Origen | ... |
    ### Enunciado
    ### Diagrama de cascada (si aplica)
    ### Casos exhaustivos
    ### Asunciones a verificar (con [VERIFICAR] / [ASUNCIÓN])
    ### Implementa
    ### Tests
    ### Reglas relacionadas
-->
`;
}

function humanize(slug: string): string {
  return slug
    .split('-')
    .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
    .join(' ');
}

async function main(): Promise<void> {
  const args = parseArgs();
  const repoRoot = findRepoRoot();
  const yamlPath = resolve(
    repoRoot,
    'cliter',
    args.boundedContext,
    `${args.aggregate}.aurora.yaml`,
  );
  if (!existsSync(yamlPath)) {
    console.error(
      `✗ No existe ${yamlPath}. ¿BC y aggregate están bien escritos? (kebab-lowercase)`,
    );
    process.exit(1);
  }

  const targetPath = resolve(
    repoRoot,
    'cliter',
    args.boundedContext,
    'business-rules',
    `${args.aggregate}.md`,
  );

  const today = new Date().toISOString().slice(0, 10);
  if (!existsSync(targetPath)) {
    mkdirSync(dirname(targetPath), { recursive: true });
    writeFileSync(targetPath, buildSkeleton(args.boundedContext, args.aggregate, today));
    console.log(`✓ Esqueleto creado: ${targetPath}`);
  } else {
    console.log(`› Archivo del catálogo ya existe: ${targetPath}`);
    console.log('  Las reglas detectadas se añadirán al final, no se sobrescribirá nada.');
  }

  const sourcePaths = await listSourcePaths(
    repoRoot,
    args.boundedContext,
    args.aggregate,
  );
  const testPaths = await listTestPaths(repoRoot, args.boundedContext, args.aggregate);

  console.log('\n=== Fuentes a consultar ===\n');
  console.log('Archivos de dominio:');
  if (sourcePaths.length === 0) {
    console.log('  (ninguno encontrado — ¿el agregado tiene código generado todavía?)');
  } else {
    for (const p of sourcePaths.slice(0, 60)) {
      console.log(`  - ${p}`);
    }
    if (sourcePaths.length > 60) {
      console.log(`  ... (+${sourcePaths.length - 60} más)`);
    }
  }
  console.log('\nTests:');
  if (testPaths.length === 0) {
    console.log('  (ninguno encontrado)');
  } else {
    for (const p of testPaths.slice(0, 30)) {
      console.log(`  - ${p}`);
    }
  }

  if (args.fromMd) {
    const fromAbs = resolve(repoRoot, args.fromMd);
    if (!existsSync(fromAbs)) {
      console.error(`\n✗ --from apunta a un fichero inexistente: ${args.fromMd}`);
      process.exit(1);
    }
    const fromContent = readFileSync(fromAbs, 'utf-8');
    console.log(
      `\n=== Material de referencia (--from ${args.fromMd}, ${fromContent.length} bytes) ===`,
    );
    console.log(
      '  Lee este archivo además de las fuentes de dominio para mapear las reglas existentes',
    );
    console.log('  al formato canónico del catálogo.');
  }

  console.log('\n=== Siguiente paso (para el LLM en Claude Code) ===\n');
  console.log('  1. Lee el .aurora.yaml para conocer estados, relaciones y campos.');
  console.log('  2. Lee CONTEXT.md (si existe) para entender el flujo del BC.');
  console.log('  3. Recorre los archivos de dominio listados; identifica:');
  console.log('     - validations (precondiciones sobre comandos)');
  console.log('     - cascades (efectos en otros agregados)');
  console.log('     - derived invariants (campos calculados)');
  console.log('     - policies (límites, ratios, excepciones)');
  console.log('  4. Para cada regla detectada:');
  console.log('     - Asigna ID `BR-<BC-CORTO>-<AGG-CORTO>-<NNN>` (siguiente disponible).');
  console.log('     - Redacta enunciado en español.');
  console.log('     - Construye tabla exhaustiva de casos. Marca [VERIFICAR]');
  console.log('       cualquier celda que no se derive claramente del código.');
  console.log('     - Cita `Implementa` con paths reales y líneas.');
  console.log('     - Cita `Tests` con archivos y nombres de test.');
  console.log('  5. Pide confirmación al usuario antes de escribir cada regla.');
  console.log('  6. Cuando termines: `pnpm br:generate && pnpm br:validate`.');
}

main().catch((err) => {
  console.error('✗ document falló:');
  console.error(err.message ?? err);
  process.exit(1);
});
