/**
 * Reads all reference and example files from the monorepo and generates
 * a TypeScript module that embeds them as a string map. This allows the
 * compiled CLI binary to work without access to the monorepo filesystem.
 *
 * Run: bun run scripts/generate-embedded.ts
 */

import { existsSync } from 'node:fs';
import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
import { dirname, join, relative, resolve } from 'node:path';

const CLI_ROOT = resolve(dirname(Bun.main), '..');
const REPO_ROOT = resolve(CLI_ROOT, '../..');

const REFERENCE_DIRS: [string, string][] = [
  ['services/platform/convex', 'convex'],
  ['services/platform/lib', 'lib'],
  ['examples', 'examples'],
];

const SKIP_DIRS = new Set(['.history', '_generated', 'node_modules']);
const SKIP_EXTENSIONS = [
  '.test.ts',
  '.test.js',
  '.spec.ts',
  // Provider secrets — the committed examples are SOPS blobs bound to a
  // single recipient and undecryptable to any other contributor; bundling
  // them adds bloat without value, and shipping plaintext credentials in
  // the binary would be a leak.
  '.secrets.json',
];
const BINARY_EXTENSIONS = new Set([
  '.png',
  '.jpg',
  '.jpeg',
  '.gif',
  '.ico',
  '.webp',
]);

function shouldSkipFile(name: string): boolean {
  if (SKIP_EXTENSIONS.some((ext) => name.endsWith(ext))) return true;
  const ext = name.slice(name.lastIndexOf('.')).toLowerCase();
  return BINARY_EXTENSIONS.has(ext);
}

async function collectFiles(
  dir: string,
  baseDir: string,
  prefix: string,
  files: Map<string, string>,
): Promise<void> {
  if (!existsSync(dir)) return;

  const entries = await readdir(dir, { withFileTypes: true });

  for (const entry of entries) {
    if (SKIP_DIRS.has(entry.name)) continue;

    const fullPath = join(dir, entry.name);

    if (entry.isDirectory()) {
      await collectFiles(fullPath, baseDir, prefix, files);
    } else {
      if (shouldSkipFile(entry.name)) continue;
      const content = await readFile(fullPath, 'utf-8');
      const relPath = join(prefix, relative(baseDir, fullPath));
      files.set(relPath, content);
    }
  }
}

async function main() {
  if (!existsSync(join(REPO_ROOT, 'examples'))) {
    console.error('Could not find monorepo root at', REPO_ROOT);
    process.exit(1);
  }

  const files = new Map<string, string>();

  for (const [srcRel, destPrefix] of REFERENCE_DIRS) {
    const srcDir = join(REPO_ROOT, srcRel);
    await collectFiles(srcDir, srcDir, destPrefix, files);
  }

  console.log(`Collected ${files.size} files for embedding`);

  // Generate TypeScript module
  const lines: string[] = [
    '// AUTO-GENERATED by scripts/generate-embedded.ts — do not edit',
    '',
    'export const EMBEDDED_REFERENCE: Record<string, string> = {',
  ];

  const referenceFiles = new Map<string, string>();
  const exampleFiles = new Map<string, string>();

  for (const [path, content] of files) {
    if (path.startsWith('examples/')) {
      exampleFiles.set(path, content);
    } else {
      referenceFiles.set(path, content);
    }
  }

  lines.push('};');
  lines.push('');
  lines.push('export const EMBEDDED_EXAMPLES: Record<string, string> = {');
  lines.push('};');

  // Build the actual content using JSON.stringify for safe escaping
  const referenceObj: Record<string, string> = {};
  for (const [path, content] of referenceFiles) {
    referenceObj[path] = content;
  }

  const exampleObj: Record<string, string> = {};
  for (const [path, content] of exampleFiles) {
    exampleObj[path] = content;
  }

  const output = [
    '// AUTO-GENERATED by scripts/generate-embedded.ts — do not edit',
    '',
    `export const EMBEDDED_REFERENCE: Record<string, string> = ${JSON.stringify(referenceObj, null, 2)};`,
    '',
    `export const EMBEDDED_EXAMPLES: Record<string, string> = ${JSON.stringify(exampleObj, null, 2)};`,
    '',
  ].join('\n');

  const outDir = join(CLI_ROOT, 'src', 'generated');
  await mkdir(outDir, { recursive: true });

  const outPath = join(outDir, 'embedded-files.ts');
  await writeFile(outPath, output, 'utf-8');

  console.log(`Written to ${relative(CLI_ROOT, outPath)}`);
  console.log(
    `  Reference: ${referenceFiles.size} files, Examples: ${exampleFiles.size} files`,
  );
}

main();
