import fs from 'fs';
import path from 'path';
import { TemplateError, TemplateNotFoundError, InvalidTemplateError } from '../errors';
import { Logger } from '../utils/logger';

export interface TemplateOptions {
  engine?: 'handlebars' | 'ejs' | 'pug';
  cache?: boolean;
  defaultLocale?: string;
}

export class TemplateEngine {
  private templates: Map<string, string> = new Map();
  private compiledTemplates: Map<string, Function> = new Map();
  private options: Required<TemplateOptions>;
  private logger: Logger;

  constructor(
    private templatesDir: string,
    options: TemplateOptions = {}
  ) {
    this.options = {
      engine: options.engine || 'handlebars',
      cache: options.cache ?? true,
      defaultLocale: options.defaultLocale || 'en',
    };
    this.logger = Logger.getInstance();
    this.validateTemplatesDir();
  }

  private validateTemplatesDir(): void {
    if (!fs.existsSync(this.templatesDir)) {
      throw new TemplateError(`Templates directory not found: ${this.templatesDir}`);
    }
  }

  private getTemplatePath(name: string, locale?: string): string {
    const localeDir = locale || this.options.defaultLocale;
    const templatePath = path.join(this.templatesDir, localeDir, `${name}.${this.options.engine}`);

    if (!fs.existsSync(templatePath)) {
      throw new TemplateNotFoundError(name, { locale, path: templatePath });
    }

    return templatePath;
  }

  private loadTemplate(name: string, locale?: string): string {
    const cacheKey = `${locale || this.options.defaultLocale}:${name}`;

    if (this.options.cache && this.templates.has(cacheKey)) {
      return this.templates.get(cacheKey)!;
    }

    const templatePath = this.getTemplatePath(name, locale);
    try {
      const template = fs.readFileSync(templatePath, 'utf-8');
      if (this.options.cache) {
        this.templates.set(cacheKey, template);
      }
      return template;
    } catch (error) {
      throw new TemplateError(`Failed to load template: ${name}`, { error });
    }
  }

  private compileTemplate(template: string): Function {
    try {
      switch (this.options.engine) {
        case 'handlebars':
          return this.compileHandlebars(template);
        case 'ejs':
          return this.compileEjs(template);
        case 'pug':
          return this.compilePug(template);
        default:
          throw new InvalidTemplateError(`Unsupported template engine: ${this.options.engine}`);
      }
    } catch (error) {
      throw new InvalidTemplateError(`Failed to compile template`, { error });
    }
  }

  private compileHandlebars(template: string): Function {
    // TODO: In a real implementation, you would import and use the Handlebars library
    return (context: any) => {
      return template.replace(/\{\{([^}]+)\}\}/g, (_match, key) => {
        return context[key.trim()] || '';
      });
    };
  }

  private compileEjs(template: string): Function {
    // TODO: In a real implementation, you would import and use the EJS library
    return (context: any) => {
      return template.replace(/<%=([^%>]+)%>/g, (_match, key) => {
        return context[key.trim()] || '';
      });
    };
  }

  private compilePug(_template: string): Function {
    // TODO: In a real implementation, you would import and use the Pug library
    throw new Error('Pug compilation not implemented');
  }

  render(name: string, context: Record<string, any>, locale?: string): string {
    this.logger.debug(`Rendering template: ${name}`, { locale, context });

    try {
      const template = this.loadTemplate(name, locale);
      const cacheKey = `${locale || this.options.defaultLocale}:${name}`;

      let compiledTemplate: Function;
      if (this.options.cache && this.compiledTemplates.has(cacheKey)) {
        compiledTemplate = this.compiledTemplates.get(cacheKey)!;
      } else {
        compiledTemplate = this.compileTemplate(template);
        if (this.options.cache) {
          this.compiledTemplates.set(cacheKey, compiledTemplate);
        }
      }

      const result = compiledTemplate(context);
      this.logger.debug(`Template rendered successfully: ${name}`);
      return result;
    } catch (error) {
      this.logger.error(`Failed to render template: ${name}`, { error, locale, context });
      throw error;
    }
  }

  clearCache(): void {
    this.templates.clear();
    this.compiledTemplates.clear();
    this.logger.debug('Template cache cleared');
  }

  setOptions(options: Partial<TemplateOptions>): void {
    this.options = {
      ...this.options,
      ...options,
    };
    this.logger.debug('Template options updated', { options });
  }

  getAvailableTemplates(locale?: string): string[] {
    const localeDir = path.join(this.templatesDir, locale || this.options.defaultLocale);
    try {
      return fs
        .readdirSync(localeDir)
        .filter(file => file.endsWith(`.${this.options.engine}`))
        .map(file => path.basename(file, `.${this.options.engine}`));
    } catch (error) {
      throw new TemplateError(`Failed to list templates`, { error, locale });
    }
  }
}
