const nodemailer = require('nodemailer');
const config = require('../config');
const { query, pool } = require('../db');
const { v4: uuidv4 } = require('uuid');

/**
 * Service for sending emails with templates
 */
const emailService = {
  /**
   * Create email transporter
   * @returns {Object} Configured nodemailer transporter
   */
  createTransporter() {
    return nodemailer.createTransport({
      host: config.email.host,
      port: config.email.port,
      auth: {
        user: config.email.user,
        pass: config.email.pass
      }
    });
  },
  defaultFrom: config.email.reply,
  siteUrl: `${config.site.protocol}://${config.site.apiDomain}`,

  /**
   * Get a template by type, preferring the default one
   * @param {string} type - Template type
   * @returns {Promise<Object|null>} Template object or null if not found
   */
  async getTemplateByType(type) {
    try {
      // Get all settings with 'email_templates' category
      const result = await query(
        'SELECT * FROM system_settings WHERE category = $1',
        ['email_templates']
      );
      
      // Find the default template for the specified type
      let defaultTemplate = null;
      let fallbackTemplate = null;
      
      for (const setting of result.rows) {
        try {
          console.log(setting.value, typeof setting.value)
          const templateData = JSON.parse(setting.value);
          
          if (templateData.type === type) {
            if (templateData.isDefault) {
              defaultTemplate = {
                id: setting.key,
                ...templateData
              };
              break; // Found the default template
            } else if (!fallbackTemplate) {
              // Keep a fallback template in case no default is found
              fallbackTemplate = {
                id: setting.key,
                ...templateData
              };
            }
          }
        } catch (e) {
          console.error(`Failed to parse template setting: ${setting.key}`, e);
        }
      }
      
      // Return default template if found, otherwise return fallback or null
      return defaultTemplate || fallbackTemplate || null;
    } catch (error) {
      console.error('Error getting template by type:', error);
      return null;
    }
  },

  /**
   * Replace template variables with actual values
   * @param {string} content - Template content
   * @param {Object} variables - Variable values
   * @returns {string} Processed content
   */
  replaceVariables(content, variables) {
    let processedContent = content;
    
    if (variables) {
      for (const [key, value] of Object.entries(variables)) {
        const placeholder = `{{${key}}}`;
        const regex = new RegExp(placeholder, 'g');
        processedContent = processedContent.replace(regex, value || '');
      }
    }
    
    return processedContent;
  },

  /**
   * Send an email using a template
   * @param {Object} options - Email options
   * @param {string} options.to - Recipient email address
   * @param {string} options.templateType - Template type
   * @param {Object} options.variables - Template variables
   * @param {string} [options.from] - Sender email (optional, defaults to config)
   * @param {string} [options.subject] - Custom subject (optional, defaults to template subject)
   * @param {string} [options.cc] - CC recipients (optional)
   * @param {string} [options.bcc] - BCC recipients (optional)
   * @returns {Promise<boolean>} Success status
   */
  async sendTemplatedEmail(options) {
    try {
      const { to, templateType, variables, from, subject, cc, bcc } = options;
      
      // Get template
      const template = await this.getTemplateByType(templateType);
      
      if (!template) {
        throw new Error(`No template found for type: ${templateType}`);
      }
      
      // Replace variables in content and subject
      const emailContent = this.replaceVariables(template.content, variables);
      const emailSubject = subject || this.replaceVariables(template.subject, variables);
      
      // Create transporter
      const transporter = this.createTransporter();
      
      // Send email
      const result = await transporter.sendMail({
        from: from || config.email.reply,
        to,
        cc,
        bcc,
        subject: emailSubject,
        html: emailContent
      });
      
      console.log(`Email sent: ${result.messageId}`);
      return true;
    } catch (error) {
      console.error('Error sending templated email:', error);
      throw error;
    }
  },
  /**
   * Send an email
   * @param {Object} options - Email options for nodemailer
   * @returns {Promise} - Promise with the send result
   */
  async sendEmail(options) {
    try {
      // Send email using nodemailer
      const transporter = this.createTransporter();
      const result = await transporter.sendMail({
        from: options.from || this.defaultFrom,
        to: options.to,
        subject: options.subject,
        html: options.html,
        text: options.text,
        attachments: options.attachments,
        headers: options.headers
      });
      
      return result;
    } catch (error) {
      console.error('Error sending email:', error);
      throw error;
    }
  },

  /**
   * Send a login code email
   * @param {Object} options - Options
   * @param {string} options.to - Recipient email
   * @param {string} options.code - Login verification code
   * @param {string} options.loginLink - Direct login link
   * @returns {Promise<boolean>} Success status
   */
  async sendLoginCodeEmail(options) {
    const { to, code, loginLink } = options;
    
    return this.sendTemplatedEmail({
      to,
      templateType: 'login_code',
      variables: {
        code,
        loginLink,
        email: to
      }
    });
  },

  /**
   * Send a shipping notification email
   * @param {Object} options - Options
   * @param {string} options.to - Recipient email
   * @param {string} options.first_name - Customer's first name
   * @param {string} options.order_id - Order ID
   * @param {string} options.tracking_number - Tracking number
   * @param {string} options.carrier - Shipping carrier
   * @param {string} options.tracking_link - Tracking link
   * @param {string} options.shipped_date - Ship date
   * @param {string} options.estimated_delivery - Estimated delivery
   * @param {string} options.items_html - Order items HTML table
   * @param {string} options.customer_message - Custom message
   * @returns {Promise<boolean>} Success status
   */
  async sendShippingNotification(options) {
    return this.sendTemplatedEmail({
      to: options.to,
      templateType: 'shipping_notification',
      variables: options
    });
  },

  /**
   * Send an order confirmation email
   * @param {Object} options - Options
   * @param {string} options.to - Recipient email
   * @param {string} options.first_name - Customer's first name
   * @param {string} options.order_id - Order ID
   * @param {string} options.order_date - Order date
   * @param {string} options.order_total - Order total
   * @param {string} options.shipping_address - Shipping address
   * @param {string} options.items_html - Order items HTML table
   * @returns {Promise<boolean>} Success status
   */
  async sendOrderConfirmation(options) {
    return this.sendTemplatedEmail({
      to: options.to,
      templateType: 'order_confirmation',
      variables: options
    });
  },

  /**
   * Send a test email for campaign preview
   * @param {Object} options - Email options
   * @param {string} options.to - Recipient email
   * @param {string} options.subject - Email subject
   * @param {string} options.preheader - Email preheader
   * @param {string} options.from - From address
   * @param {string} options.content - HTML content
   * @returns {Promise} - Promise with the send result
   */
  async sendTestEmail({ to, subject, preheader, from, content }) {
    try {
      // Add preheader if provided
      let htmlContent = content;
      if (preheader) {
        htmlContent = `
          <div style="display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">
            ${preheader}
          </div>
          ${content}
        `;
      }
      
      // Add test label at top
      htmlContent = `
        <div style="background-color:#ffeb3b;padding:10px;margin-bottom:10px;text-align:center;font-family:sans-serif;">
          <strong>TEST EMAIL</strong> - This is a test preview of your campaign.
        </div>
        ${htmlContent}
      `;
      
      // Send the test email
      return await this.sendEmail({
        to,
        subject,
        html: htmlContent,
        from: from || this.defaultFrom,
        headers: {
          'X-Test-Email': 'true'
        }
      });
    } catch (error) {
      console.error('Error sending test email:', error);
      throw error;
    }
  },
  /**
   * Send a low stock alert email
   * @param {Object} options - Options
   * @param {string} options.to - Recipient email
   * @param {string} options.product_name - Product name
   * @param {string} options.current_stock - Current stock level
   * @param {string} options.threshold - Stock threshold
   * @returns {Promise<boolean>} Success status
   */
  async sendLowStockAlert(options) {
    return this.sendTemplatedEmail({
      to: options.to,
      templateType: 'low_stock_alert',
      variables: options
    });
  },

  /**
   * Send a welcome email
   * @param {Object} options - Options
   * @param {string} options.to - Recipient email
   * @param {string} options.first_name - User's first name
   * @returns {Promise<boolean>} Success status
   */
  async sendWelcomeEmail(options) {
    return this.sendTemplatedEmail({
      to: options.to,
      templateType: 'welcome_email',
      variables: {
        first_name: options.first_name,
        email: options.to
      }
    });
  },
  /**
   * Send a subscription confirmation email
   * @param {Object} options - Email options
   * @param {string} options.to - Recipient email
   * @param {string} options.firstName - Recipient's first name
   * @param {string} options.confirmationLink - Confirmation link
   * @returns {Promise} - Promise with the send result
   */
  async sendSubscriptionConfirmation({ to, firstName, confirmationLink }) {
    try {
      // Get the email template
      const template = await this.getEmailTemplate('subscription_confirmation');
      
      if (!template) {
        // Fallback to a basic template if none is found
        return await this.sendEmail({
          to,
          subject: 'Please confirm your subscription',
          html: `
            <h1>Confirm Your Subscription</h1>
            <p>Hello ${firstName || 'there'},</p>
            <p>Thank you for subscribing to our mailing list. Please click the link below to confirm your subscription:</p>
            <p><a href="${confirmationLink}">Confirm Subscription</a></p>
            <p>If you did not request this subscription, you can ignore this email.</p>
          `,
          from: this.defaultFrom
        });
      }
      
      // Replace placeholders in the template
      let content = template.content;
      let subject = template.subject;
      
      content = content
        .replace(/{{first_name}}/g, firstName || 'there')
        .replace(/{{confirmation_link}}/g, confirmationLink);
      
      subject = subject
        .replace(/{{first_name}}/g, firstName || 'there');
      
      // Send email
      return await this.sendEmail({
        to,
        subject,
        html: content,
        from: template.from_name ? `${template.from_name} <${this.defaultFrom}>` : this.defaultFrom
      });
    } catch (error) {
      console.error('Error sending subscription confirmation email:', error);
      throw error;
    }
  },
  /**
   * Send an email to a campaign subscriber
   * @param {Object} options - Email options
   * @param {string} options.to - Recipient email
   * @param {string} options.subject - Email subject
   * @param {string} options.preheader - Email preheader
   * @param {string} options.from - From address
   * @param {string} options.content - HTML content
   * @param {string} options.campaignId - Campaign ID
   * @param {string} options.subscriberId - Subscriber ID
   * @returns {Promise} - Promise with the send result
   */
  async sendCampaignEmail({ to, subject, preheader, from, content, campaignId, subscriberId }) {
    try {
      // Generate tracking pixel and add unsubscribe link
      const trackingPixel = this.generateTrackingPixel(campaignId, subscriberId);
      const unsubscribeLink = this.generateUnsubscribeLink(subscriberId, campaignId);
      
      // Add preheader if provided
      let htmlContent = content;
      if (preheader) {
        htmlContent = `
          <div style="display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">
            ${preheader}
          </div>
          ${htmlContent}
        `;
      }
      
      // Add tracking pixel and unsubscribe footer
      htmlContent = `
        ${htmlContent}
        <div style="margin-top:20px;padding:20px;font-family:sans-serif;font-size:12px;color:#666;text-align:center;border-top:1px solid #eee;">
          <p>If you no longer wish to receive these emails, you can <a href="${unsubscribeLink}">unsubscribe here</a>.</p>
          <p>Sent by ${config.site.domain}</p>
        </div>
        ${trackingPixel}
      `;
      
      // Process links to add tracking
      htmlContent = await this.processLinks(htmlContent, campaignId, subscriberId);
      
      // Send the campaign email
      return await this.sendEmail({
        to,
        subject,
        html: htmlContent,
        from: from || this.defaultFrom,
        headers: {
          'X-Campaign-ID': campaignId,
          'List-Unsubscribe': `<${unsubscribeLink}>`,
          'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click'
        }
      });
    } catch (error) {
      console.error('Error sending campaign email:', error);
      throw error;
    }
  },
  
  /**
   * Generate a tracking pixel for email opens
   * @param {string} campaignId - Campaign ID
   * @param {string} subscriberId - Subscriber ID
   * @returns {string} - HTML for the tracking pixel
   */
  generateTrackingPixel(campaignId, subscriberId) {
    const trackingUrl = `${this.siteUrl}/api/email/track?c=${campaignId}&s=${subscriberId}&t=open`;
    return `<img src="${trackingUrl}" width="1" height="1" alt="" style="display:block;width:1px;height:1px;border:0;" />`;
  },
  
  /**
   * Generate an unsubscribe link
   * @param {string} subscriberId - Subscriber ID
   * @param {string} campaignId - Campaign ID
   * @returns {string} - Unsubscribe URL
   */
  generateUnsubscribeLink(subscriberId, campaignId) {
    // Generate an unsubscribe token
    const token = uuidv4();
    
    
    this.storeUnsubscribeToken(subscriberId, token, campaignId);
    
    return `${this.siteUrl}/api/subscribers/unsubscribe?token=${token}`;
  },
  
  /**
   * Store an unsubscribe token in the database
   * @param {string} subscriberId - Subscriber ID
   * @param {string} token - Unsubscribe token
   * @param {string} campaignId - Campaign ID
   */
  async storeUnsubscribeToken(subscriberId, token, campaignId) {
    try {
      // Set token to expire in 60 days
      const expiresAt = new Date();
      expiresAt.setDate(expiresAt.getDate() + 60);
      
      await query(
        `INSERT INTO unsubscribe_tokens (subscriber_id, token, campaign_id, expires_at)
         VALUES ($1, $2, $3, $4)`,
        [subscriberId, token, campaignId, expiresAt]
      );
    } catch (error) {
      console.error('Error storing unsubscribe token:', error);
    }
  },
  
  /**
   * Process HTML content to add tracking to links
   * @param {string} content - HTML content
   * @param {string} campaignId - Campaign ID
   * @param {string} subscriberId - Subscriber ID
   * @returns {string} - HTML with tracked links
   */
  async processLinks(content, campaignId, subscriberId) {
    try {
      // Basic regex to find links - in a production environment, use a proper HTML parser
      const linkRegex = /<a[^>]+href=["']([^"']+)["'][^>]*>([^<]+)<\/a>/gi;
      let match;
      let processedContent = content;
      
      // Store links found in this content
      const links = [];
      
      // First pass: collect all links
      while ((match = linkRegex.exec(content)) !== null) {
        const url = match[1];
        const text = match[2];
        
        // Skip tracking for unsubscribe links and mailto links
        if (url.includes('/unsubscribe') || url.startsWith('mailto:')) {
          continue;
        }
        
        // Store the link info
        links.push({ url, text });
      }
      
      // Store links in the database and get their IDs
      const linkIds = await this.storeLinks(links, campaignId);
      
      // Second pass: replace links with tracked versions
      let index = 0;
      processedContent = content.replace(linkRegex, (match, url, text) => {
        // Skip tracking for unsubscribe links and mailto links
        if (url.includes('/unsubscribe') || url.startsWith('mailto:')) {
          return match;
        }
        
        const linkId = linkIds[index++];
        if (!linkId) return match; // Skip if no linkId (shouldn't happen)
        
        // Create tracking URL
        const trackingUrl = `${this.siteUrl}/api/email/track?c=${campaignId}&s=${subscriberId}&l=${linkId}&t=click&u=${encodeURIComponent(url)}`;
        
        // Replace the original URL with the tracking URL
        return match.replace(url, trackingUrl);
      });
      
      return processedContent;
    } catch (error) {
      console.error('Error processing links:', error);
      return content; // Return original content on error
    }
  },
  
  /**
   * Store links in the database
   * @param {Array} links - Array of link objects { url, text }
   * @param {string} campaignId - Campaign ID
   * @returns {Array} - Array of link IDs
   */
  async storeLinks(links, campaignId) {
    try {
      const linkIds = [];
      
      for (const link of links) {
        // Check if this link already exists for this campaign
        const existingLinkCheck = await query(
          'SELECT id FROM campaign_links WHERE campaign_id = $1 AND url = $2',
          [campaignId, link.url]
        );
        
        if (existingLinkCheck.rows.length > 0) {
          // Link already exists, use its ID
          linkIds.push(existingLinkCheck.rows[0].id);
        } else {
          // Link doesn't exist, create a new one
          const linkId = uuidv4();
          await query(
            `INSERT INTO campaign_links (id, campaign_id, url, text)
             VALUES ($1, $2, $3, $4)`,
            [linkId, campaignId, link.url, link.text]
          );
          linkIds.push(linkId);
        }
      }
      
      return linkIds;
    } catch (error) {
      console.error('Error storing links:', error);
      return [];
    }
  },

  /**
   * Log an email in the database
   * @param {Object} emailData - Email data to log
   * @param {string} emailData.recipient - Recipient email
   * @param {string} emailData.subject - Email subject
   * @param {string} emailData.sent_by - User ID who sent the email
   * @param {string} [emailData.template_id] - Template ID used
   * @param {string} [emailData.template_type] - Template type used
   * @returns {Promise<Object>} Log entry
   */
  async logEmail(emailData) {
    try {
      const result = await query(
        `INSERT INTO email_logs 
         (recipient, subject, sent_by, template_id, template_type, status) 
         VALUES ($1, $2, $3, $4, $5, $6)
         RETURNING *`,
        [
          emailData.recipient,
          emailData.subject,
          emailData.sent_by,
          emailData.template_id || null,
          emailData.template_type || null,
          'sent'
        ]
      );
      
      return result.rows[0];
    } catch (error) {
      console.error('Error logging email:', error);
      // Don't throw error, just log it
      return null;
    }
  },

    /**
   * Send a refund confirmation email
   * @param {Object} options - Options
   * @param {string} options.to - Recipient email
   * @param {string} options.first_name - Customer's first name
   * @param {string} options.order_id - Order ID
   * @param {string} options.refund_amount - Refund amount
   * @param {string} options.refund_date - Refund date
   * @param {string} options.refund_method - Refund method (e.g., "Original payment method")
   * @param {string} options.refund_reason - Reason for refund
   * @returns {Promise<boolean>} Success status
   */
  async sendRefundConfirmation(options) {
    return this.sendTemplatedEmail({
      to: options.to,
      templateType: 'refund_confirmation',
      variables: options
    });
  }
};

module.exports = emailService;