import { logger } from './logger';

/**
 * Resolves various node type input formats to all possible template node type formats.
 * Templates store node types in full n8n format (e.g., "n8n-nodes-base.slack").
 * This function handles various input formats and expands them to all possible matches.
 * 
 * @param nodeTypes Array of node types in various formats
 * @returns Array of all possible template node type formats
 * 
 * @example
 * resolveTemplateNodeTypes(['slack']) 
 * // Returns: ['n8n-nodes-base.slack', 'n8n-nodes-base.slackTrigger']
 * 
 * resolveTemplateNodeTypes(['nodes-base.webhook'])
 * // Returns: ['n8n-nodes-base.webhook']
 * 
 * resolveTemplateNodeTypes(['httpRequest'])
 * // Returns: ['n8n-nodes-base.httpRequest']
 */
export function resolveTemplateNodeTypes(nodeTypes: string[]): string[] {
  const resolvedTypes = new Set<string>();
  
  for (const nodeType of nodeTypes) {
    // Add all variations for this node type
    const variations = generateTemplateNodeVariations(nodeType);
    variations.forEach(v => resolvedTypes.add(v));
  }
  
  const result = Array.from(resolvedTypes);
  logger.debug(`Resolved ${nodeTypes.length} input types to ${result.length} template variations`, {
    input: nodeTypes,
    output: result
  });
  
  return result;
}

/**
 * Generates all possible template node type variations for a single input.
 * 
 * @param nodeType Single node type in any format
 * @returns Array of possible template formats
 */
function generateTemplateNodeVariations(nodeType: string): string[] {
  const variations = new Set<string>();
  
  // If it's already in full n8n format, just return it
  if (nodeType.startsWith('n8n-nodes-base.') || nodeType.startsWith('@n8n/n8n-nodes-langchain.')) {
    variations.add(nodeType);
    return Array.from(variations);
  }
  
  // Handle partial prefix formats (e.g., "nodes-base.slack" -> "n8n-nodes-base.slack")
  if (nodeType.startsWith('nodes-base.')) {
    const nodeName = nodeType.replace('nodes-base.', '');
    variations.add(`n8n-nodes-base.${nodeName}`);
    // Also try camelCase variations
    addCamelCaseVariations(variations, nodeName, 'n8n-nodes-base');
  } else if (nodeType.startsWith('nodes-langchain.')) {
    const nodeName = nodeType.replace('nodes-langchain.', '');
    variations.add(`@n8n/n8n-nodes-langchain.${nodeName}`);
    // Also try camelCase variations
    addCamelCaseVariations(variations, nodeName, '@n8n/n8n-nodes-langchain');
  } else if (!nodeType.includes('.')) {
    // Bare node name (e.g., "slack", "webhook", "httpRequest")
    // Try both packages with various case combinations
    
    // For n8n-nodes-base
    variations.add(`n8n-nodes-base.${nodeType}`);
    addCamelCaseVariations(variations, nodeType, 'n8n-nodes-base');
    
    // For langchain (less common for bare names, but include for completeness)
    variations.add(`@n8n/n8n-nodes-langchain.${nodeType}`);
    addCamelCaseVariations(variations, nodeType, '@n8n/n8n-nodes-langchain');
    
    // Add common related node types (e.g., "slack" -> also include "slackTrigger")
    addRelatedNodeTypes(variations, nodeType);
  }
  
  return Array.from(variations);
}

/**
 * Adds camelCase variations for a node name.
 * 
 * @param variations Set to add variations to
 * @param nodeName The node name to create variations for
 * @param packagePrefix The package prefix to use
 */
function addCamelCaseVariations(variations: Set<string>, nodeName: string, packagePrefix: string): void {
  const lowerName = nodeName.toLowerCase();
  
  // Common patterns in n8n node names
  const patterns = [
    // Pattern: somethingTrigger (e.g., slackTrigger, webhookTrigger)
    { suffix: 'trigger', capitalize: true },
    { suffix: 'Trigger', capitalize: false },
    // Pattern: somethingRequest (e.g., httpRequest)
    { suffix: 'request', capitalize: true },
    { suffix: 'Request', capitalize: false },
    // Pattern: somethingDatabase (e.g., mysqlDatabase, postgresDatabase)
    { suffix: 'database', capitalize: true },
    { suffix: 'Database', capitalize: false },
    // Pattern: somethingSheet/Sheets (e.g., googleSheets)
    { suffix: 'sheet', capitalize: true },
    { suffix: 'Sheet', capitalize: false },
    { suffix: 'sheets', capitalize: true },
    { suffix: 'Sheets', capitalize: false },
  ];
  
  // Check if the lowercase name matches any pattern
  for (const pattern of patterns) {
    const lowerSuffix = pattern.suffix.toLowerCase();
    
    if (lowerName.endsWith(lowerSuffix)) {
      // Name already has the suffix, try different capitalizations
      const baseName = lowerName.slice(0, -lowerSuffix.length);
      if (baseName) {
        if (pattern.capitalize) {
          // Capitalize the suffix
          const capitalizedSuffix = pattern.suffix.charAt(0).toUpperCase() + pattern.suffix.slice(1).toLowerCase();
          variations.add(`${packagePrefix}.${baseName}${capitalizedSuffix}`);
        } else {
          // Use the suffix as-is
          variations.add(`${packagePrefix}.${baseName}${pattern.suffix}`);
        }
      }
    } else if (!lowerName.includes(lowerSuffix)) {
      // Name doesn't have the suffix, try adding it
      if (pattern.capitalize) {
        const capitalizedSuffix = pattern.suffix.charAt(0).toUpperCase() + pattern.suffix.slice(1).toLowerCase();
        variations.add(`${packagePrefix}.${lowerName}${capitalizedSuffix}`);
      }
    }
  }
  
  // Handle specific known cases
  const specificCases: Record<string, string[]> = {
    'http': ['httpRequest'],
    'httprequest': ['httpRequest'],
    'mysql': ['mysql', 'mysqlDatabase'],
    'postgres': ['postgres', 'postgresDatabase'],
    'postgresql': ['postgres', 'postgresDatabase'],
    'mongo': ['mongoDb', 'mongodb'],
    'mongodb': ['mongoDb', 'mongodb'],
    'google': ['googleSheets', 'googleDrive', 'googleCalendar'],
    'googlesheet': ['googleSheets'],
    'googlesheets': ['googleSheets'],
    'microsoft': ['microsoftTeams', 'microsoftExcel', 'microsoftOutlook'],
    'slack': ['slack'],
    'discord': ['discord'],
    'telegram': ['telegram'],
    'webhook': ['webhook'],
    'schedule': ['scheduleTrigger'],
    'cron': ['cron', 'scheduleTrigger'],
    'email': ['emailSend', 'emailReadImap', 'gmail'],
    'gmail': ['gmail', 'gmailTrigger'],
    'code': ['code'],
    'javascript': ['code'],
    'python': ['code'],
    'js': ['code'],
    'set': ['set'],
    'if': ['if'],
    'switch': ['switch'],
    'merge': ['merge'],
    'loop': ['splitInBatches'],
    'split': ['splitInBatches', 'splitOut'],
    'ai': ['openAi'],
    'openai': ['openAi'],
    'chatgpt': ['openAi'],
    'gpt': ['openAi'],
    'api': ['httpRequest', 'graphql', 'webhook'],
    'csv': ['spreadsheetFile', 'readBinaryFile'],
    'excel': ['microsoftExcel', 'spreadsheetFile'],
    'spreadsheet': ['spreadsheetFile', 'googleSheets', 'microsoftExcel'],
  };
  
  const cases = specificCases[lowerName];
  if (cases) {
    cases.forEach(c => variations.add(`${packagePrefix}.${c}`));
  }
}

/**
 * Adds related node types for common patterns.
 * For example, "slack" should also include "slackTrigger".
 * 
 * @param variations Set to add variations to
 * @param nodeName The base node name
 */
function addRelatedNodeTypes(variations: Set<string>, nodeName: string): void {
  const lowerName = nodeName.toLowerCase();
  
  // Map of base names to their related node types
  const relatedTypes: Record<string, string[]> = {
    'slack': ['slack', 'slackTrigger'],
    'gmail': ['gmail', 'gmailTrigger'],
    'telegram': ['telegram', 'telegramTrigger'],
    'discord': ['discord', 'discordTrigger'],
    'webhook': ['webhook', 'webhookTrigger'],
    'http': ['httpRequest', 'webhook'],
    'email': ['emailSend', 'emailReadImap', 'gmail', 'gmailTrigger'],
    'google': ['googleSheets', 'googleDrive', 'googleCalendar', 'googleDocs'],
    'microsoft': ['microsoftTeams', 'microsoftExcel', 'microsoftOutlook', 'microsoftOneDrive'],
    'database': ['postgres', 'mysql', 'mongoDb', 'redis', 'postgresDatabase', 'mysqlDatabase'],
    'db': ['postgres', 'mysql', 'mongoDb', 'redis'],
    'sql': ['postgres', 'mysql', 'mssql'],
    'nosql': ['mongoDb', 'redis', 'couchDb'],
    'schedule': ['scheduleTrigger', 'cron'],
    'time': ['scheduleTrigger', 'cron', 'wait'],
    'file': ['readBinaryFile', 'writeBinaryFile', 'moveBinaryFile'],
    'binary': ['readBinaryFile', 'writeBinaryFile', 'moveBinaryFile'],
    'csv': ['spreadsheetFile', 'readBinaryFile'],
    'excel': ['microsoftExcel', 'spreadsheetFile'],
    'json': ['code', 'set'],
    'transform': ['code', 'set', 'merge', 'splitInBatches'],
    'ai': ['openAi', 'agent', 'lmChatOpenAi', 'lmChatAnthropic'],
    'llm': ['openAi', 'agent', 'lmChatOpenAi', 'lmChatAnthropic', 'lmChatGoogleGemini'],
    'agent': ['agent', 'toolAgent'],
    'chat': ['chatTrigger', 'agent'],
  };
  
  const related = relatedTypes[lowerName];
  if (related) {
    related.forEach(r => {
      variations.add(`n8n-nodes-base.${r}`);
      // Also check if it might be a langchain node
      if (['agent', 'toolAgent', 'chatTrigger', 'lmChatOpenAi', 'lmChatAnthropic', 'lmChatGoogleGemini'].includes(r)) {
        variations.add(`@n8n/n8n-nodes-langchain.${r}`);
      }
    });
  }
}