import type { IRouteContext } from '../models/route-context.js';

/**
 * Utility class for resolving template variables in strings
 */
export class TemplateUtils {
  /**
   * Resolve template variables in a string using the route context
   * Supports variables like {domain}, {path}, {clientIp}, etc.
   * 
   * @param template The template string with {variables}
   * @param context The route context with values
   * @returns The resolved string
   */
  public static resolveTemplateVariables(template: string, context: IRouteContext): string {
    if (!template) {
      return template;
    }
    
    // Replace variables with values from context
    return template.replace(/\{([a-zA-Z0-9_\.]+)\}/g, (match, varName) => {
      // Handle nested properties with dot notation (e.g., {headers.host})
      if (varName.includes('.')) {
        const parts = varName.split('.');
        let current: any = context;
        
        // Traverse nested object structure
        for (const part of parts) {
          if (current === undefined || current === null) {
            return match; // Return original if path doesn't exist
          }
          current = current[part];
        }
        
        // Return the resolved value if it exists
        if (current !== undefined && current !== null) {
          return TemplateUtils.convertToString(current);
        }
        
        return match;
      }

      // Direct property access
      const value = context[varName as keyof IRouteContext];
      if (value === undefined) {
        return match; // Keep the original {variable} if not found
      }

      // Convert value to string
      return TemplateUtils.convertToString(value);
    });
  }
  
  /**
   * Safely convert a value to a string
   * 
   * @param value Any value to convert to string
   * @returns String representation or original match for complex objects
   */
  private static convertToString(value: any): string {
    if (value === null || value === undefined) {
      return '';
    }
    
    if (typeof value === 'string') {
      return value;
    }
    
    if (typeof value === 'number' || typeof value === 'boolean') {
      return value.toString();
    }
    
    if (Array.isArray(value)) {
      return value.join(',');
    }
    
    if (typeof value === 'object') {
      try {
        return JSON.stringify(value);
      } catch (e) {
        return '[Object]';
      }
    }
    
    return String(value);
  }
  
  /**
   * Resolve template variables in header values
   * 
   * @param headers Header object with potential template variables
   * @param context Route context for variable resolution
   * @returns New header object with resolved values
   */
  public static resolveHeaderTemplates(
    headers: Record<string, string>,
    context: IRouteContext
  ): Record<string, string> {
    const result: Record<string, string> = {};
    
    for (const [key, value] of Object.entries(headers)) {
      // Skip special directive headers (starting with !)
      if (value.startsWith('!')) {
        result[key] = value;
        continue;
      }
      
      // Resolve template variables in the header value
      result[key] = TemplateUtils.resolveTemplateVariables(value, context);
    }
    
    return result;
  }
  
  /**
   * Check if a string contains template variables
   * 
   * @param str String to check for template variables
   * @returns True if string contains template variables
   */
  public static containsTemplateVariables(str: string): boolean {
    return !!str && /\{([a-zA-Z0-9_\.]+)\}/g.test(str);
  }
}