#!/usr/bin/env node

/**
 * MORPH-SPEC Template Renderer
 *
 * Renders templates by replacing {{PLACEHOLDER}} with actual values.
 *
 * Usage:
 *   node bin/render-template.js <template-path> <output-path> <variables-json>
 *
 * Example:
 *   node bin/render-template.js \
 *     content/.morph/templates/spec.md \
 *     .morph/project/outputs/my-feature/spec.md \
 *     '{"FEATURE_NAME":"my-feature","STACK":"Blazor","DATE":"2024-01-15"}'
 */

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// ANSI color codes
const colors = {
  green: '\x1b[32m',
  red: '\x1b[31m',
  yellow: '\x1b[33m',
  cyan: '\x1b[36m',
  reset: '\x1b[0m'
};

/**
 * Load config to get default values
 */
function loadConfig() {
  const configPaths = [
    path.join(process.cwd(), '.morph/config/config.json'),
    path.join(process.cwd(), 'content/.morph/config/config.json'),
    path.join(__dirname, '../content/.morph/config/config.template.json')
  ];

  for (const configPath of configPaths) {
    if (fs.existsSync(configPath)) {
      try {
        const content = fs.readFileSync(configPath, 'utf-8');
        return JSON.parse(content);
      } catch (error) {
        // Continue to next path
      }
    }
  }

  return null;
}

/**
 * Get default variables from config and environment
 */
function getDefaultVariables() {
  const config = loadConfig();
  const now = new Date();

  return {
    DATE: now.toISOString().split('T')[0],
    YEAR: now.getFullYear().toString(),
    AUTHOR: config?.project?.author || 'MORPH-SPEC',
    PROJECT_NAME: config?.project?.name || 'Project',
    STACK: config?.project?.stack || 'Blazor',
    NAMESPACE: config?.project?.namespace || 'Project'
  };
}

/**
 * Convert string to various case formats
 */
function transformCase(str) {
  return {
    // kebab-case → PascalCase
    pascal: str
      .split('-')
      .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
      .join(''),

    // kebab-case → camelCase
    camel: str
      .split('-')
      .map((word, index) =>
        index === 0
          ? word.toLowerCase()
          : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
      )
      .join(''),

    // kebab-case → Title Case
    title: str
      .split('-')
      .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
      .join(' '),

    // kebab-case → UPPER_SNAKE_CASE
    upperSnake: str.toUpperCase().replace(/-/g, '_'),

    // kebab-case → lower_snake_case
    lowerSnake: str.toLowerCase().replace(/-/g, '_')
  };
}

/**
 * Render template by replacing placeholders
 */
function renderTemplate(templatePath, variables) {
  if (!fs.existsSync(templatePath)) {
    throw new Error(`Template file not found: ${templatePath}`);
  }

  let content = fs.readFileSync(templatePath, 'utf-8');

  // Get default variables
  const defaults = getDefaultVariables();

  // Merge with provided variables (provided takes precedence)
  const allVariables = { ...defaults, ...variables };

  // Auto-generate case transformations for FEATURE_NAME if provided
  if (allVariables.FEATURE_NAME) {
    const featureName = allVariables.FEATURE_NAME;
    const transformations = transformCase(featureName);

    allVariables.FEATURE_NAME_PASCAL = transformations.pascal;
    allVariables.FEATURE_NAME_CAMEL = transformations.camel;
    allVariables.FEATURE_NAME_TITLE = transformations.title;
    allVariables.FEATURE_NAME_UPPER_SNAKE = transformations.upperSnake;
    allVariables.FEATURE_NAME_LOWER_SNAKE = transformations.lowerSnake;

    // Backward compatibility: FEATURE_TITLE maps to FEATURE_NAME_TITLE
    if (!allVariables.FEATURE_TITLE) {
      allVariables.FEATURE_TITLE = transformations.title;
    }
  }

  // Track which placeholders were replaced
  const replacedPlaceholders = new Set();
  const unreplacedPlaceholders = new Set();

  // Replace all placeholders
  for (const [key, value] of Object.entries(allVariables)) {
    const placeholder = `{{${key}}}`;
    if (content.includes(placeholder)) {
      content = content.replaceAll(placeholder, value);
      replacedPlaceholders.add(key);
    }
  }

  // Find unreplaced placeholders
  const placeholderRegex = /\{\{([A-Z_]+)\}\}/g;
  let match;
  while ((match = placeholderRegex.exec(content)) !== null) {
    unreplacedPlaceholders.add(match[1]);
  }

  return {
    content,
    replacedPlaceholders: Array.from(replacedPlaceholders),
    unreplacedPlaceholders: Array.from(unreplacedPlaceholders)
  };
}

/**
 * Print help message
 */
function printHelp() {
  console.log(`
${colors.cyan}MORPH-SPEC Template Renderer${colors.reset}

${colors.green}Usage:${colors.reset}
  node bin/render-template.js <template-path> <output-path> <variables-json>

${colors.green}Arguments:${colors.reset}
  template-path    Path to the template file (required)
  output-path      Path where rendered output will be written (required)
  variables-json   JSON object with variables to replace (required)

${colors.green}Standard Placeholders:${colors.reset}
  {{FEATURE_NAME}}           - kebab-case feature name (e.g., "scheduled-reports")
  {{FEATURE_NAME_PASCAL}}    - PascalCase (e.g., "ScheduledReports")
  {{FEATURE_NAME_CAMEL}}     - camelCase (e.g., "scheduledReports")
  {{FEATURE_NAME_TITLE}}     - Title Case (e.g., "Scheduled Reports")
  {{FEATURE_NAME_UPPER_SNAKE}} - UPPER_SNAKE_CASE (e.g., "SCHEDULED_REPORTS")
  {{FEATURE_NAME_LOWER_SNAKE}} - lower_snake_case (e.g., "scheduled_reports")
  {{FEATURE_TITLE}}          - Alias for FEATURE_NAME_TITLE
  {{STACK}}                  - Project stack (e.g., "Blazor", "Next.js")
  {{DATE}}                   - Current date (YYYY-MM-DD)
  {{YEAR}}                   - Current year
  {{AUTHOR}}                 - Author name (from config.json)
  {{PROJECT_NAME}}           - Project name (from config.json)
  {{NAMESPACE}}              - C# namespace (from config.json)

${colors.green}Example:${colors.reset}
  node bin/render-template.js \\
    content/.morph/templates/spec.md \\
    .morph/project/outputs/scheduled-reports/spec.md \\
    '{"FEATURE_NAME":"scheduled-reports","STACK":"Blazor"}'

${colors.green}Flags:${colors.reset}
  --help, -h       Show this help message
  --verbose, -v    Show detailed replacement information
  --dry-run, -d    Preview output without writing file

${colors.yellow}Note:${colors.reset} DATE, YEAR, AUTHOR, PROJECT_NAME, STACK, NAMESPACE are auto-populated from config.json
  `);
}

/**
 * Main CLI logic
 */
function main() {
  const args = process.argv.slice(2);

  // Check for help flag
  if (args.includes('--help') || args.includes('-h')) {
    printHelp();
    process.exit(0);
  }

  // Parse flags
  const verbose = args.includes('--verbose') || args.includes('-v');
  const dryRun = args.includes('--dry-run') || args.includes('-d');

  // Filter out flags
  const flags = ['--verbose', '-v', '--dry-run', '-d', '--help', '-h'];
  const cleanArgs = args.filter(arg => !flags.includes(arg));

  // Validate arguments
  if (cleanArgs.length < 3) {
    console.error(`${colors.red}❌ Error: Missing required arguments${colors.reset}\n`);
    printHelp();
    process.exit(1);
  }

  const [templatePath, outputPath, variablesJson] = cleanArgs;

  // Parse variables JSON
  let variables;
  try {
    variables = JSON.parse(variablesJson);
  } catch (error) {
    console.error(`${colors.red}❌ Error: Invalid JSON in variables argument${colors.reset}`);
    console.error(`   ${error.message}\n`);
    process.exit(1);
  }

  try {
    // Render template
    const { content, replacedPlaceholders, unreplacedPlaceholders } = renderTemplate(
      templatePath,
      variables
    );

    // Dry run: just preview
    if (dryRun) {
      console.log(`${colors.cyan}📋 DRY RUN - Preview (file will NOT be written)${colors.reset}\n`);
      console.log(content);
      console.log(`\n${colors.cyan}═══════════════════════════════════════════${colors.reset}`);
    } else {
      // Create output directory if it doesn't exist
      const outputDir = path.dirname(outputPath);
      if (!fs.existsSync(outputDir)) {
        fs.mkdirSync(outputDir, { recursive: true });
      }

      // Write rendered content
      fs.writeFileSync(outputPath, content, 'utf-8');
      console.log(`${colors.green}✅ Template rendered successfully${colors.reset}`);
      console.log(`   Output: ${outputPath}`);
    }

    // Verbose output
    if (verbose || unreplacedPlaceholders.length > 0) {
      console.log(`\n${colors.cyan}📊 Replacement Summary:${colors.reset}`);
      console.log(`   ${colors.green}Replaced:${colors.reset} ${replacedPlaceholders.length} placeholders`);

      if (replacedPlaceholders.length > 0) {
        console.log(`   ${replacedPlaceholders.map(p => `{{${p}}}`).join(', ')}`);
      }

      if (unreplacedPlaceholders.length > 0) {
        console.log(`\n   ${colors.yellow}⚠️  Unreplaced:${colors.reset} ${unreplacedPlaceholders.length} placeholders`);
        console.log(`   ${unreplacedPlaceholders.map(p => `{{${p}}}`).join(', ')}`);
      }
    }

    process.exit(0);
  } catch (error) {
    console.error(`${colors.red}❌ Error: ${error.message}${colors.reset}`);
    process.exit(1);
  }
}

// Run CLI
main();
