#!/usr/bin/env -S npx --yes tsx
// =============================================================================
// render.ts — Render a template with vault variable substitution
//
// Part of: .opencode/skills/templating-vault/scripts/
//
// Two modes:
//   1. File mode (--output-file): Renders template, writes to file, returns path
//   2. Prepare mode (--prepare --id): Renders template, stores in vault under
//      prepared.<id>, returns only the ID. For vault-aware prompt-by-reference.
//
// USAGE:
//   # File mode (vault-unaware agents)
//   render.ts --template-key <key> --scope <tag> --output-file <path>
//
//   # Prepare mode (vault-aware agents)
//   render.ts --template-key <key> --scope <tag> --prepare --id <id>
//
//   # Template from file instead of vault
//   render.ts --template-file <path> --scope <tag> --output-file <path>
//
// OUTPUT (stdout): JSON result
// =============================================================================
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import yaml from 'js-yaml';
import {
  parseCommonFlags, getFlag, hasFlag, output, log,
  loadConfig, readValue, getValue, writeValue,
  loadSchema, saveKeyMeta, listAllKeys, loadKeyMeta, readValue,
  evaluateTemplate, atomicWrite,
  type TemplateSchema, type KeyMeta,
} from './api.ts';

async function main(): Promise<void> {
  const { config: cfgPath, storeDirs, rest } = parseCommonFlags(process.argv);
  const config = loadConfig(cfgPath, storeDirs.length > 0 ? storeDirs : undefined);

  const templateKey = getFlag(rest, '--template-key');
  const templateFile = getFlag(rest, '--template-file');
  const outputFile = getFlag(rest, '--output-file');
  const prepare = hasFlag(rest, '--prepare');
  let id = getFlag(rest, '--id');

  if (!templateKey && !templateFile) {
    output({ rendered: false, error: '--template-key or --template-file is required' });
    process.exit(1);
  }

  // Auto-generate ID if --prepare but no --id
  if (prepare && !id) {
    id = `prep-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
  }

  if (!prepare && !outputFile) {
    output({ rendered: false, error: '--output-file is required when not using --prepare' });
    process.exit(1);
  }

  // Load template text
  let templateText: string;
  let schema: TemplateSchema | null = null;

  if (templateKey) {
    const raw = readValue(config, templateKey);
    if (raw === null) {
      output({ rendered: false, error: `Template key "${templateKey}" not found in vault` });
      process.exit(1);
    }
    templateText = raw;
    schema = loadSchema(config, templateKey);
  } else {
    if (!fs.existsSync(templateFile!)) {
      output({ rendered: false, error: `Template file "${templateFile}" not found` });
      process.exit(1);
    }
    templateText = fs.readFileSync(templateFile!, 'utf-8');
  }

  // Collect variables for rendering
  const variables: Record<string, unknown> = {};
  const substituted: string[] = [];
  const missing: string[] = [];

  // Collect RAW values from all vault stores (skip templates to avoid recursion)
  const allKeys = listAllKeys(config, '', true);
  for (const k of allKeys) {
    const meta = loadKeyMeta(config, k);
    if (meta.template) continue; // Skip computed/template keys
    const rawVal = readValue(config, k);
    if (rawVal !== null) {
      const nunjucksKey = k.replace(/\./g, '_');
      variables[nunjucksKey] = rawVal;
      const shortName = k.split('.').pop()!;
      if (!(shortName in variables)) {
        variables[shortName] = rawVal;
      }
    }
  }

  // Detect which placeholders are in the template
  const placeholderRegex = /\{\{\s*(\w+)(?:\s*\|[^}]*)?\s*\}\}/g;
  let match: RegExpExecArray | null;
  const foundPlaceholders = new Set<string>();
  while ((match = placeholderRegex.exec(templateText)) !== null) {
    foundPlaceholders.add(match[1]);
  }

  for (const ph of foundPlaceholders) {
    if (ph in variables) {
      substituted.push(ph);
    } else {
      missing.push(ph);
    }
  }

  // Render template
  let rendered: string;
  try {
    rendered = evaluateTemplate(templateText, variables);
  } catch (err) {
    output({
      rendered: false,
      error: `Template rendering failed: ${err instanceof Error ? err.message : String(err)}`,
    });
    process.exit(1);
  }

  if (prepare) {
    // Store in vault under prepared.<id>
    const preparedKey = `prepared.${id}`;
    writeValue(config, preparedKey, rendered);

    // Store metadata
    const meta: KeyMeta = {
      template: false,
      owner: 'system',
      description: `Prepared prompt from template ${templateKey ?? templateFile}`,
    };
    saveKeyMeta(config, preparedKey, meta);

    // Store schema if available
    if (schema) {
      const schemaKey = `prepared.${id}.schema`;
      writeValue(config, schemaKey, JSON.stringify(schema, null, 2));
    }

    // Store template source reference
    const refKey = `prepared.${id}.template_ref`;
    writeValue(config, refKey, templateKey ?? templateFile ?? 'unknown');

    output({
      prepared: true,
      id,
      key: preparedKey,
      substituted,
      missing,
    });
  } else {
    // Write to file
    const outDir = require('node:path').dirname(outputFile!);
    fs.mkdirSync(outDir, { recursive: true });
    atomicWrite(outputFile!, rendered);

    output({
      rendered: true,
      output_file: outputFile,
      substituted,
      missing,
    });
  }
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
  main().catch((err: unknown) => {
    output({ rendered: false, error: String(err instanceof Error ? err.message : err) });
    process.exit(1);
  });
}
