/**
 * Template Renderer - Shared Handlebars-based template engine.
 *
 * Provides unified template rendering with custom helpers for:
 * - Case transformations (pascalCase, camelCase, snakeCase, etc.)
 * - Conditional logic (eq, ne, and, or, not)
 * - Loops (each, unless)
 *
 * @module template-renderer
 */

import Handlebars from 'handlebars';
import { readFileSync, existsSync } from 'fs';

/**
 * Register all custom Handlebars helpers
 */
function registerHelpers() {
  // Case transformation helpers
  Handlebars.registerHelper('pascalCase', function (str) {
    if (typeof str !== 'string') return '';
    return str
      .split('-')
      .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
      .join('');
  });

  Handlebars.registerHelper('camelCase', function (str) {
    if (typeof str !== 'string') return '';
    return str
      .split('-')
      .map((word, index) =>
        index === 0
          ? word.toLowerCase()
          : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
      )
      .join('');
  });

  Handlebars.registerHelper('titleCase', function (str) {
    if (typeof str !== 'string') return '';
    return str
      .split('-')
      .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
      .join(' ');
  });

  Handlebars.registerHelper('upperSnakeCase', function (str) {
    if (typeof str !== 'string') return '';
    return str.toUpperCase().replace(/-/g, '_');
  });

  Handlebars.registerHelper('snakeCase', function (str) {
    if (typeof str !== 'string') return '';
    return str.toLowerCase().replace(/-/g, '_');
  });

  Handlebars.registerHelper('kebabCase', function (str) {
    if (typeof str !== 'string') return '';
    return str.toLowerCase().replace(/_/g, '-');
  });

  // Comparison helpers
  Handlebars.registerHelper('eq', function (a, b) {
    return a === b;
  });

  Handlebars.registerHelper('ne', function (a, b) {
    return a !== b;
  });

  Handlebars.registerHelper('lt', function (a, b) {
    return a < b;
  });

  Handlebars.registerHelper('gt', function (a, b) {
    return a > b;
  });

  Handlebars.registerHelper('lte', function (a, b) {
    return a <= b;
  });

  Handlebars.registerHelper('gte', function (a, b) {
    return a >= b;
  });

  // Logical helpers
  Handlebars.registerHelper('and', function (...args) {
    // Remove Handlebars options object (last argument)
    const values = args.slice(0, -1);
    return values.every(Boolean);
  });

  Handlebars.registerHelper('or', function (...args) {
    const values = args.slice(0, -1);
    return values.some(Boolean);
  });

  Handlebars.registerHelper('not', function (value) {
    return !value;
  });

  // String helpers
  Handlebars.registerHelper('uppercase', function (str) {
    return typeof str === 'string' ? str.toUpperCase() : '';
  });

  Handlebars.registerHelper('lowercase', function (str) {
    return typeof str === 'string' ? str.toLowerCase() : '';
  });

  Handlebars.registerHelper('capitalize', function (str) {
    if (typeof str !== 'string') return '';
    return str.charAt(0).toUpperCase() + str.slice(1);
  });

  // Utility helpers
  Handlebars.registerHelper('default', function (value, defaultValue) {
    return value != null && value !== '' ? value : defaultValue;
  });

  Handlebars.registerHelper('json', function (obj) {
    return JSON.stringify(obj, null, 2);
  });

  Handlebars.registerHelper('now', function (format) {
    const date = new Date();
    if (format === 'iso') {
      return date.toISOString();
    } else if (format === 'date') {
      return date.toISOString().split('T')[0];
    } else if (format === 'time') {
      return date.toTimeString().split(' ')[0];
    }
    return date.toISOString().split('T')[0]; // Default: YYYY-MM-DD
  });

  Handlebars.registerHelper('year', function () {
    return new Date().getFullYear();
  });

  // Date formatting helpers
  Handlebars.registerHelper('formatDate', function (date, format) {
    if (!date) return '';

    const d = new Date(date);
    if (isNaN(d.getTime())) return date; // Return original if invalid

    switch (format) {
      case 'iso':
        return d.toISOString();
      case 'date':
        return d.toISOString().split('T')[0]; // YYYY-MM-DD
      case 'time':
        return d.toTimeString().split(' ')[0]; // HH:MM:SS
      case 'datetime':
        return d.toISOString().replace('T', ' ').split('.')[0]; // YYYY-MM-DD HH:MM:SS
      case 'short':
        return d.toLocaleDateString(); // Locale-specific short date
      case 'long':
        return d.toLocaleDateString('en-US', {
          weekday: 'long',
          year: 'numeric',
          month: 'long',
          day: 'numeric'
        });
      default:
        return d.toISOString().split('T')[0]; // Default: YYYY-MM-DD
    }
  });

  // Pluralization helpers (basic English rules)
  Handlebars.registerHelper('pluralize', function (str) {
    if (typeof str !== 'string') return '';

    // Common irregular plurals
    const irregulars = {
      'person': 'people',
      'child': 'children',
      'foot': 'feet',
      'tooth': 'teeth',
      'mouse': 'mice',
      'goose': 'geese'
    };

    const lower = str.toLowerCase();
    if (irregulars[lower]) {
      return str[0] === str[0].toUpperCase()
        ? irregulars[lower].charAt(0).toUpperCase() + irregulars[lower].slice(1)
        : irregulars[lower];
    }

    // Regular pluralization rules
    if (str.endsWith('s') || str.endsWith('x') || str.endsWith('z') ||
        str.endsWith('ch') || str.endsWith('sh')) {
      return str + 'es';
    } else if (str.endsWith('y') && !/[aeiou]y$/i.test(str)) {
      return str.slice(0, -1) + 'ies';
    } else if (str.endsWith('f')) {
      return str.slice(0, -1) + 'ves';
    } else if (str.endsWith('fe')) {
      return str.slice(0, -2) + 'ves';
    } else {
      return str + 's';
    }
  });

  Handlebars.registerHelper('singularize', function (str) {
    if (typeof str !== 'string') return '';

    // Common irregular singulars
    const irregulars = {
      'people': 'person',
      'children': 'child',
      'feet': 'foot',
      'teeth': 'tooth',
      'mice': 'mouse',
      'geese': 'goose'
    };

    const lower = str.toLowerCase();
    if (irregulars[lower]) {
      return str[0] === str[0].toUpperCase()
        ? irregulars[lower].charAt(0).toUpperCase() + irregulars[lower].slice(1)
        : irregulars[lower];
    }

    // Regular singularization rules
    if (str.endsWith('ies')) {
      return str.slice(0, -3) + 'y';
    } else if (str.endsWith('ves')) {
      return str.slice(0, -3) + 'f';
    } else if (str.endsWith('es')) {
      return str.slice(0, -2);
    } else if (str.endsWith('s') && !str.endsWith('ss')) {
      return str.slice(0, -1);
    } else {
      return str;
    }
  });

  // String manipulation helpers
  Handlebars.registerHelper('trim', function (str) {
    return typeof str === 'string' ? str.trim() : '';
  });

  Handlebars.registerHelper('replace', function (str, search, replacement) {
    if (typeof str !== 'string') return '';
    return str.replace(new RegExp(search, 'g'), replacement);
  });

  Handlebars.registerHelper('concat', function (...args) {
    // Remove Handlebars options object (last argument)
    const strings = args.slice(0, -1);
    return strings.join('');
  });

  Handlebars.registerHelper('substr', function (str, start, length) {
    if (typeof str !== 'string') return '';
    return length !== undefined ? str.substring(start, start + length) : str.substring(start);
  });

  Handlebars.registerHelper('startsWith', function (str, prefix) {
    if (typeof str !== 'string') return false;
    return str.startsWith(prefix);
  });

  Handlebars.registerHelper('endsWith', function (str, suffix) {
    if (typeof str !== 'string') return false;
    return str.endsWith(suffix);
  });

  Handlebars.registerHelper('contains', function (str, substring) {
    if (typeof str !== 'string') return false;
    return str.includes(substring);
  });

  Handlebars.registerHelper('length', function (value) {
    if (Array.isArray(value) || typeof value === 'string') {
      return value.length;
    }
    return 0;
  });

  // Math helpers
  Handlebars.registerHelper('add', function (a, b) {
    return Number(a) + Number(b);
  });

  Handlebars.registerHelper('subtract', function (a, b) {
    return Number(a) - Number(b);
  });

  Handlebars.registerHelper('multiply', function (a, b) {
    return Number(a) * Number(b);
  });

  Handlebars.registerHelper('divide', function (a, b) {
    return Number(b) !== 0 ? Number(a) / Number(b) : 0;
  });

  Handlebars.registerHelper('mod', function (a, b) {
    return Number(a) % Number(b);
  });

  Handlebars.registerHelper('round', function (num, decimals = 0) {
    const multiplier = Math.pow(10, decimals);
    return Math.round(Number(num) * multiplier) / multiplier;
  });

  // Array helpers
  Handlebars.registerHelper('join', function (array, separator = ', ') {
    if (!Array.isArray(array)) return '';
    return array.join(separator);
  });

  Handlebars.registerHelper('first', function (array) {
    if (!Array.isArray(array) || array.length === 0) return undefined;
    return array[0];
  });

  Handlebars.registerHelper('last', function (array) {
    if (!Array.isArray(array) || array.length === 0) return undefined;
    return array[array.length - 1];
  });

  Handlebars.registerHelper('slice', function (array, start, end) {
    if (!Array.isArray(array)) return [];
    return array.slice(start, end);
  });
}

// Auto-register helpers on module load
registerHelpers();

/**
 * Render a template file with Handlebars.
 *
 * @param {string} templatePath - Absolute path to template file
 * @param {Object} variables - Variables to pass to template context
 * @param {Object} [options] - Rendering options
 * @param {boolean} [options.noEscape] - Disable HTML escaping (default: true for markdown)
 * @returns {string} Rendered template content
 * @throws {Error} If template file doesn't exist or compilation fails
 */
export function renderTemplate(templatePath, variables, options = {}) {
  if (!existsSync(templatePath)) {
    throw new Error(`Template file not found: ${templatePath}`);
  }

  try {
    const templateContent = readFileSync(templatePath, 'utf-8');

    // Compile template
    const template = Handlebars.compile(templateContent, {
      noEscape: options.noEscape !== undefined ? options.noEscape : true, // Don't escape by default
      strict: false, // Allow undefined variables
      preventIndent: true, // Preserve indentation
    });

    // Render with variables
    return template(variables);
  } catch (error) {
    throw new Error(`Failed to render template ${templatePath}: ${error.message}`);
  }
}

/**
 * Render a template string (not from file) with Handlebars.
 *
 * @param {string} templateString - Template content as string
 * @param {Object} variables - Variables to pass to template context
 * @param {Object} [options] - Rendering options
 * @returns {string} Rendered content
 */
export function renderTemplateString(templateString, variables, options = {}) {
  try {
    const template = Handlebars.compile(templateString, {
      noEscape: options.noEscape !== undefined ? options.noEscape : true,
      strict: false,
      preventIndent: true,
    });

    return template(variables);
  } catch (error) {
    throw new Error(`Failed to render template string: ${error.message}`);
  }
}

/**
 * Get list of placeholders used in a template file.
 *
 * @param {string} templatePath - Absolute path to template file
 * @returns {string[]} Array of placeholder names found
 */
export function getTemplatePlaceholders(templatePath) {
  if (!existsSync(templatePath)) {
    throw new Error(`Template file not found: ${templatePath}`);
  }

  const templateContent = readFileSync(templatePath, 'utf-8');
  return getTemplatePlaceholdersFromString(templateContent);
}

/**
 * Get list of placeholders from a template string.
 *
 * @param {string} templateString - Template content
 * @returns {string[]} Array of placeholder names found
 */
export function getTemplatePlaceholdersFromString(templateString) {
  const placeholders = new Set();

  // Match {{PLACEHOLDER}} and {{helper PLACEHOLDER}}
  const regex = /\{\{(?:#|\/)?\s*([A-Za-z_][A-Za-z0-9_]*)/g;
  let match;

  while ((match = regex.exec(templateString)) !== null) {
    const placeholder = match[1];

    // Skip Handlebars built-in helpers and custom helpers
    const skipHelpers = [
      // Built-in Handlebars
      'if', 'unless', 'each', 'with', 'lookup', 'log', 'else',
      // Comparison
      'eq', 'ne', 'lt', 'gt', 'lte', 'gte',
      // Logical
      'and', 'or', 'not',
      // Case transformations
      'pascalCase', 'camelCase', 'titleCase', 'upperSnakeCase', 'snakeCase', 'kebabCase',
      // String manipulation
      'uppercase', 'lowercase', 'capitalize', 'trim', 'replace', 'concat', 'substr',
      'startsWith', 'endsWith', 'contains', 'length',
      // Date/Time
      'formatDate', 'now', 'year',
      // Pluralization
      'pluralize', 'singularize',
      // Math
      'add', 'subtract', 'multiply', 'divide', 'mod', 'round',
      // Array
      'join', 'first', 'last', 'slice',
      // Utility
      'default', 'json'
    ];

    if (!skipHelpers.includes(placeholder)) {
      placeholders.add(placeholder);
    }
  }

  return Array.from(placeholders).sort();
}

/**
 * Validate that all required placeholders are provided in variables.
 *
 * @param {string} templatePath - Absolute path to template file
 * @param {Object} variables - Variables object
 * @param {string[]} [optionalPlaceholders] - Placeholders that are optional
 * @returns {{ valid: boolean, missing: string[] }} Validation result
 */
export function validateTemplatePlaceholders(templatePath, variables, optionalPlaceholders = []) {
  const placeholders = getTemplatePlaceholders(templatePath);
  const providedKeys = Object.keys(variables);

  const missing = placeholders.filter(
    p => !providedKeys.includes(p) && !optionalPlaceholders.includes(p)
  );

  return {
    valid: missing.length === 0,
    missing
  };
}

export { Handlebars };
