import { ITemplateService, TemplateInfo, TemplateValidationResult } from '../../interfaces/template.interface';
import { EmailProviderType } from '../../interfaces/email-options.interface';
import { SesConfig } from '../../interfaces/email-options.interface';
import { Logger } from '../../utils/logger';
import { TemplateError, InvalidTemplateError } from '../../errors';
import { SESClient } from '@aws-sdk/client-ses';
const { GetTemplateCommand, ListTemplatesCommand } = require('@aws-sdk/client-ses');

export class SesTemplateService implements ITemplateService {
  private logger: Logger;
  private config: SesConfig;
  private client: SESClient;

  constructor(config: SesConfig) {
    this.config = config;
    this.logger = Logger.getInstance();
    this.client = this.createSesClient();
  }

  async renderTemplate(
    templateName: string, 
    data: Record<string, any>, 
    provider: EmailProviderType
  ): Promise<string> {
    if (provider !== EmailProviderType.SES) {
      throw new TemplateError(`Provider ${provider} not supported by SesTemplateService`);
    }

    try {
      this.logger.debug(`Rendering SES template: ${templateName}`, { data });
      
      const template = await this.getTemplate(templateName);
      const renderedContent = this.renderWithData(template, data);
      
      this.logger.debug(`SES template rendered successfully: ${templateName}`);
      return renderedContent;
    } catch (error) {
      this.logger.error(`Failed to render SES template: ${templateName}`, { error });
      throw new TemplateError(`Failed to render SES template: ${templateName}`, { error });
    }
  }

  async validateTemplate(templateName: string, provider: EmailProviderType): Promise<boolean> {
    if (provider !== EmailProviderType.SES) {
      return false;
    }

    try {
      await this.getTemplate(templateName);
      return true;
    } catch {
      return false;
    }
  }

  async getTemplateInfo(templateName: string, provider: EmailProviderType): Promise<TemplateInfo> {
    if (provider !== EmailProviderType.SES) {
      throw new TemplateError(`Provider ${provider} not supported by SesTemplateService`);
    }

    try {
      const template = await this.getTemplate(templateName);
      
      return {
        id: templateName,
        name: template.Template?.TemplateName || templateName,
        version: template.Template?.Version || '1.0.0',
        variables: this.extractVariables(template.Template?.HtmlPart || ''),
        lastModified: template.Template?.LastModifiedDate || new Date(),
        provider: EmailProviderType.SES
      };
    } catch (error) {
      throw new TemplateError(`Failed to get template info: ${templateName}`, { error });
    }
  }

  async validateTemplateWithDetails(templateName: string, data: Record<string, any>): Promise<TemplateValidationResult> {
    try {
      const template = await this.getTemplate(templateName);
      const templateVariables = this.extractVariables(template.Template?.HtmlPart || '');
      const providedVariables = Object.keys(data);
      
      const missingVariables = templateVariables.filter(v => !providedVariables.includes(v));
      const warnings = providedVariables.filter(v => !templateVariables.includes(v));
      
      return {
        isValid: missingVariables.length === 0,
        errors: missingVariables.length > 0 ? [`Missing required variables: ${missingVariables.join(', ')}`] : [],
        warnings: warnings.length > 0 ? [`Unused variables provided: ${warnings.join(', ')}`] : [],
        variables: templateVariables,
        missingVariables
      };
    } catch (error) {
      return {
        isValid: false,
        errors: [`Template not found or invalid: ${templateName}`],
        warnings: [],
        variables: [],
        missingVariables: []
      };
    }
  }

  async listTemplates(): Promise<string[]> {
    try {
      const command = new ListTemplatesCommand({});
      const response = await this.client.send(command);
      
      return response.TemplatesMetadata?.map((t: any) => t.Name || '') || [];
    } catch (error) {
      this.logger.error('Failed to list SES templates', { error });
      throw new TemplateError('Failed to list SES templates', { error });
    }
  }

  private createSesClient(): SESClient {
    const clientConfig: any = {
      region: this.config.region,
      apiVersion: this.config.apiVersion || '2010-12-01',
      maxAttempts: this.config.maxRetries || 3,
    };

    if (this.config.credentials) {
      clientConfig.credentials = this.config.credentials;
    } else if (this.config.accessKeyId && this.config.secretAccessKey) {
      clientConfig.credentials = {
        accessKeyId: this.config.accessKeyId,
        secretAccessKey: this.config.secretAccessKey,
        sessionToken: this.config.sessionToken,
      };
    }

    if (this.config.endpoint) {
      clientConfig.endpoint = this.config.endpoint;
    }

    return new SESClient(clientConfig);
  }

  private async getTemplate(templateName: string): Promise<any> {
    try {
      const command = new GetTemplateCommand({ TemplateName: templateName });
      const response = await this.client.send(command);
      
      if (!response.Template) {
        throw new InvalidTemplateError(`SES template not found: ${templateName}`);
      }
      
      return response;
    } catch (error: any) {
      if (error.name === 'TemplateDoesNotExist') {
        throw new InvalidTemplateError(`SES template does not exist: ${templateName}`);
      }
      throw error;
    }
  }

  private extractVariables(htmlContent: string): string[] {
    // Extract variables from SES template format
    // SES uses {{variable}} format
    const variableRegex = /\{\{([^}]+)\}\}/g;
    const variables: string[] = [];
    let match;
    
    while ((match = variableRegex.exec(htmlContent)) !== null) {
      const variable = match[1].trim();
      if (!variables.includes(variable)) {
        variables.push(variable);
      }
    }
    
    return variables;
  }

  private renderWithData(template: any, data: Record<string, any>): string {
    let content = template.Template?.HtmlPart || template.Template?.TextPart || '';
    
    // Replace variables with data
    Object.entries(data).forEach(([key, value]) => {
      const regex = new RegExp(`{{${key}}}`, 'g');
      content = content.replace(regex, String(value));
    });
    
    // Replace any remaining variables with empty strings
    const remainingVariables = content.match(/\{\{([^}]+)\}\}/g) || [];
    remainingVariables.forEach((variable: string) => {
      const key = variable.slice(2, -2); // Remove {{ }}
      const regex = new RegExp(`{{${key}}}`, 'g');
      content = content.replace(regex, '');
    });
    
    return content;
  }
}
