const fs = require("fs").promises;
const path = require("path");
const logger = require("../../../utils/logger");
const { escapeHtml } = require("./emailUtils");

/**
 * TemplateManager handles loading, caching, and rendering email templates
 * This class is responsible for:
 * - Loading HTML email templates from disk
 * - Caching templates in memory for performance
 * - Rendering templates with variable substitution
 * - Providing fallback templates when files can't be loaded
 */
// Critical templates that must be preloaded at startup for auth flows
const CRITICAL_TEMPLATES = [
  "emailVerificationToUser",
  "passwordResetToUser",
  "passwordChangedToUser",
  "personalInfoChangedToUser",
];

class TemplateManager {
  constructor() {
    // Singleton pattern - return existing instance if already created
    if (TemplateManager.instance) {
      return TemplateManager.instance;
    }

    this.templates = new Map(); // Cached template content
    this.templateNames = new Set(); // Discovered template names
    this.initialized = false;
    this.initializationPromise = null;
    this.templatesDir = path.join(
      __dirname,
      "..",
      "..",
      "..",
      "templates",
      "emails"
    );

    TemplateManager.instance = this;
  }

  /**
   * Discover all available templates by scanning the templates directory
   * Only reads filenames, not content (for fast startup)
   * @returns {Promise<void>}
   */
  async discoverTemplates() {
    try {
      const files = await fs.readdir(this.templatesDir);
      for (const file of files) {
        if (file.endsWith(".html")) {
          this.templateNames.add(file.replace(".html", ""));
        }
      }
      logger.info("Discovered email templates", {
        count: this.templateNames.size,
      });
    } catch (error) {
      logger.error("Failed to discover email templates", {
        templatesDir: this.templatesDir,
        error,
      });
      throw error;
    }
  }

  /**
   * Load a single template from disk (lazy loading)
   * @param {string} templateName - Name of the template (without .html extension)
   * @returns {Promise<string>} Template content
   */
  async loadTemplate(templateName) {
    // Return cached template if already loaded
    if (this.templates.has(templateName)) {
      return this.templates.get(templateName);
    }

    const templatePath = path.join(this.templatesDir, `${templateName}.html`);
    try {
      const content = await fs.readFile(templatePath, "utf-8");
      this.templates.set(templateName, content);
      logger.debug("Loaded template", { templateName });
      return content;
    } catch (error) {
      logger.error("Failed to load template", {
        templateName,
        templatePath,
        error,
      });
      throw error;
    }
  }

  /**
   * Initialize the template manager by discovering templates and preloading critical ones
   * @returns {Promise<void>}
   */
  async initialize() {
    // If already initialized, return immediately
    if (this.initialized) return;

    // If initialization is in progress, wait for it
    if (this.initializationPromise) {
      return this.initializationPromise;
    }

    // Start initialization and store the promise
    this.initializationPromise = (async () => {
      // Discover all available templates (fast - only reads filenames)
      await this.discoverTemplates();

      // Preload critical templates for auth flows
      const missingCritical = [];
      for (const templateName of CRITICAL_TEMPLATES) {
        if (!this.templateNames.has(templateName)) {
          missingCritical.push(templateName);
        } else {
          await this.loadTemplate(templateName);
        }
      }

      if (missingCritical.length > 0) {
        const error = new Error(
          `Critical email templates not found: ${missingCritical.join(", ")}`
        );
        error.missingTemplates = missingCritical;
        throw error;
      }

      this.initialized = true;
      logger.info("Email Template Manager initialized successfully", {
        discovered: this.templateNames.size,
        preloaded: CRITICAL_TEMPLATES.length,
      });
    })();

    return this.initializationPromise;
  }

  /**
   * Render a template with the provided variables
   * @param {string} templateName - Name of the template to render
   * @param {Object} variables - Variables to substitute in the template
   * @returns {Promise<string>} Rendered HTML
   */
  async renderTemplate(templateName, variables = {}) {
    // Ensure service is initialized before rendering
    if (!this.initialized) {
      logger.debug("Template manager not initialized yet, initializing now...");
      await this.initialize();
    }

    let template;

    // Check if template exists in our discovered templates
    if (this.templateNames.has(templateName)) {
      // Lazy load the template if not already cached
      template = await this.loadTemplate(templateName);
    } else {
      logger.error("Template not found, using fallback", {
        templateName,
        discoveredTemplates: Array.from(this.templateNames),
      });
      template = this.getFallbackTemplate(templateName);
    }

    let rendered = template;

    try {
      Object.keys(variables).forEach((key) => {
        // Variables ending in 'Html' or 'Section' contain trusted HTML content
        // (e.g., refundSection, stripeSection, earningsSection) - don't escape these
        const isTrustedHtml = key.endsWith("Html") || key.endsWith("Section");
        let value = variables[key] || "";

        // Escape HTML in user-provided values to prevent XSS
        if (!isTrustedHtml && typeof value === "string") {
          value = escapeHtml(value);
        }

        const regex = new RegExp(`{{${key}}}`, "g");
        rendered = rendered.replace(regex, value);
      });
    } catch (error) {
      logger.error("Error rendering template", {
        templateName,
        variableKeys: Object.keys(variables),
        error,
      });
    }

    return rendered;
  }

  /**
   * Get a generic fallback template when the HTML file is not available
   * This is used as a last resort when a template cannot be loaded
   * @param {string} templateName - Name of the template (for logging)
   * @returns {string} Generic fallback HTML template
   */
  getFallbackTemplate(templateName) {
    logger.warn("Using generic fallback template", { templateName });

    return `
      <!DOCTYPE html>
      <html>
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Village Share</title>
        <style>
          body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }
          .container { max-width: 600px; margin: 0 auto; background-color: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
          .header { text-align: center; border-bottom: 2px solid #e9ecef; padding-bottom: 20px; margin-bottom: 30px; }
          .logo { font-size: 24px; font-weight: bold; color: #333; }
          .content { line-height: 1.6; color: #555; }
          .footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #e9ecef; text-align: center; font-size: 12px; color: #6c757d; }
        </style>
      </head>
      <body>
        <div class="container">
          <div class="header">
            <div class="logo">Village Share</div>
          </div>
          <div class="content">
            <p>Hi {{recipientName}},</p>
            <h2>{{title}}</h2>
            <p>{{message}}</p>
          </div>
          <div class="footer">
            <p>This email was sent from Village Share. If you have any questions, please contact support.</p>
          </div>
        </div>
      </body>
      </html>
    `;
  }
}

module.exports = TemplateManager;
