import nodemailer from 'nodemailer';
import { RateLimiterMemory } from 'rate-limiter-flexible';
import { createHash } from 'crypto';
import 'dotenv/config';
import fs from 'fs/promises';
import path from 'path';

// Environment variables
const {
  SMTP_HOST = 'mailcowdockerized-postfix-mailcow-1',
  SMTP_PORT = '587',
  SMTP_USER = 'test@devnet365.eu',
  SMTP_PASS = 'Liable7-Flavored6-Path0-Diligent0-Fragrant7',
  ADMIN_EMAIL = 'info@devnet365.eu',
  WEBSITE_NAME = '365DevNet Support',
} = process.env;

// Email configuration
const isProduction = process.env.NODE_ENV === 'production';

// Debug: Log environment variables on load (only in development or if SMTP_HOST is set)
if (!isProduction || SMTP_HOST) {
  console.log('[EMAIL CONFIG] SMTP_HOST:', SMTP_HOST || '(not set)');
  console.log('[EMAIL CONFIG] SMTP_PORT:', SMTP_PORT || '(using default: 587)');
  console.log('[EMAIL CONFIG] SMTP_USER:', SMTP_USER ? '***' : '(not set - no auth)');
  console.log('[EMAIL CONFIG] ADMIN_EMAIL:', ADMIN_EMAIL || '(not set)');
}

// Create a transporter for sending emails
let transporter: nodemailer.Transporter;

// Initialize the transporter based on environment
function initializeTransporter() {
  if (isProduction && SMTP_HOST) {
    const port = parseInt(SMTP_PORT, 10) || 587;
    // Determine if secure connection (port 465 typically uses SSL)
    const secure = port === 465;
    
    // Build transporter config
    const transporterConfig: {
      host: string;
      port: number;
      secure: boolean;
      tls: { rejectUnauthorized: boolean };
      auth?: { user: string; pass: string };
    } = {
      host: SMTP_HOST,
      port: port,
      secure: secure,
      tls: {
        rejectUnauthorized: false, // Accept self-signed certificates (useful for Mailcow)
      },
    };

    // Add authentication if credentials are provided
    if (SMTP_USER && SMTP_PASS) {
      transporterConfig.auth = {
        user: SMTP_USER,
        pass: SMTP_PASS,
      };
    }

    transporter = nodemailer.createTransport(transporterConfig);

    transporter.verify((error, _success) => {
      if (error) {
        console.error('❌ SMTP connection error:', error.message);
        console.error('   Host:', SMTP_HOST);
        console.error('   Port:', port);
        console.error('   Auth:', SMTP_USER ? 'Yes' : 'No');
      } else {
        console.log('✅ SMTP server is ready to take messages.');
        console.log('   Host:', SMTP_HOST);
        console.log('   Port:', port);
        console.log('   Auth:', SMTP_USER ? 'Yes' : 'No');
      }
    });
  } else {
    // Fallback for development: log email output to console
    transporter = nodemailer.createTransport({
      streamTransport: true,
      newline: 'unix',
      buffer: true,
    });

    console.log('⚠️ Email transporter using streamTransport (development mode)');
  }
}

// Rate limiter configuration
const rateLimiter = new RateLimiterMemory({
  points: 20, // 20 attempts
  duration: 300, // per 5 minutes
});

// CSRF protection
const csrfTokens = new Map<string, { token: string; expires: Date }>();

// Generate a CSRF token
export function generateCsrfToken(): string {
  const token = createHash('sha256').update(Math.random().toString()).digest('hex');

  // Token expires after 1 hour
  const expires = new Date();
  expires.setHours(expires.getHours() + 1);

  csrfTokens.set(token, { token, expires });

  // Clean up expired tokens
  for (const [key, value] of csrfTokens.entries()) {
    if (value.expires < new Date()) {
      csrfTokens.delete(key);
    }
  }

  return token;
}

// Validate a CSRF token
export function validateCsrfToken(token: string): boolean {
  const storedToken = csrfTokens.get(token);

  if (!storedToken) {
    return false;
  }

  if (storedToken.expires < new Date()) {
    csrfTokens.delete(token);
    return false;
  }

  return true;
}

// Check rate limit for an IP address
export async function checkRateLimit(ipAddress: string): Promise<{ limited: boolean; message?: string }> {
  try {
    await rateLimiter.consume(ipAddress);
    return { limited: false };
  } catch (error) {
    if (error instanceof Error) {
      return {
        limited: true,
        message: 'Too many requests. Please try again later.',
      };
    }
    // RateLimiterRes with msBeforeNext property
    interface RateLimiterResponse {
      msBeforeNext: number;
    }
    const resetTime = Math.ceil((error as RateLimiterResponse).msBeforeNext / 1000 / 60);
    return {
      limited: true,
      message: `Too many requests. Please try again in ${resetTime} minutes.`,
    };
  }
}

// Log email sending attempts
export function logEmailAttempt(success: boolean, recipient: string, subject: string, error?: Error): void {
  const timestamp = new Date().toISOString();
  const status = success ? 'SUCCESS' : 'FAILURE';
  const errorMessage = error ? `: ${error.message}` : '';

  const logMessage = `[${timestamp}] [EMAIL ${status}] To: ${recipient}, Subject: ${subject}${errorMessage}`;

  if (isProduction) {
    // In production, you might want to log to a file or a logging service
    console.log(logMessage);
  } else {
    // In development, log to console
    console.log(logMessage);
  }
}

// Send an email
export async function sendEmail(to: string, subject: string, html: string, text: string, _domain?: string): Promise<boolean> {
  // Initialize transporter if not already done
  if (!transporter) {
    initializeTransporter();
  }

  try {
    // Never trust user-provided domain for From header to prevent spoofing.
    const safeSender = SMTP_USER || ADMIN_EMAIL;
    const fromAddress = isProduction
      ? `"${WEBSITE_NAME}" <${safeSender}>`
      : `"${WEBSITE_NAME}" <${ADMIN_EMAIL}>`;

    const mailOptions = {
      from: fromAddress,
      to,
      subject,
      html,
      text,
    };

    // Log debug information only in non-production
    if (!isProduction) {
      console.log(`[MAILER DEBUG] Sending via: ${SMTP_HOST}:${SMTP_PORT}`);
      console.log(`[MAILER DEBUG] From: ${fromAddress} → To: ${to}`);
    }

    await transporter.sendMail(mailOptions);

    logEmailAttempt(true, to, subject);
    return true;
  } catch (error) {
    logEmailAttempt(false, to, subject, error as Error);
    console.error('Full SMTP error stack:', error);
    return false;
  }
}

// Utility to escape HTML special characters
export function escapeHtml(str: string): string {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

async function getTemplate(templateName: string, data: Record<string, string>): Promise<string> {
  const templatePath = path.join(process.cwd(), 'src', 'templates', 'email', `${templateName}.html`);
  let template = await fs.readFile(templatePath, 'utf-8');
  for (const key in data) {
    template = template.replace(new RegExp(`{{${key}}}`, 'g'), data[key]);
  }
  return template;
}

// Send admin notification email
export async function sendAdminNotification(
  name: string,
  email: string,
  message: string,
  ipAddress?: string,
  userAgent?: string,
  domain?: string
): Promise<boolean> {
  // Validate inputs
  if (!name || name.trim() === '') {
    console.error('Cannot send admin notification: name is empty');
    return false;
  }

  if (!email || email.trim() === '') {
    console.error('Cannot send admin notification: email is empty');
    return false;
  }

  if (!message || message.trim() === '') {
    console.error('Cannot send admin notification: message is empty');
    return false;
  }

  const subject = `New Contact Form Submission from ${escapeHtml(name)}`;
  const html = await getTemplate('admin-notification', {
    name: escapeHtml(name),
    email: escapeHtml(email),
    message: escapeHtml(message).replace(/\n/g, '<br>'),
    ipAddress: escapeHtml(ipAddress || ''),
    userAgent: escapeHtml(userAgent || ''),
    time: new Date().toLocaleString(),
    websiteName: WEBSITE_NAME,
  });
  const text = `
New Contact Form Submission

Name: ${name}
Email: ${email}
Message:
${message}
${ipAddress ? `IP Address: ${ipAddress}` : ''}
${userAgent ? `User Agent: ${userAgent}` : ''}
Time: ${new Date().toLocaleString()}

This message was sent from the contact form on ${WEBSITE_NAME}
  `;

  return sendEmail(ADMIN_EMAIL, subject, html, text, domain);
}

// Send user confirmation email
export async function sendUserConfirmation(name: string, email: string, message: string, domain?: string): Promise<boolean> {
  // Validate inputs
  if (!name || name.trim() === '') {
    console.error('Cannot send user confirmation: name is empty');
    return false;
  }

  if (!email || email.trim() === '') {
    console.error('Cannot send user confirmation: email is empty');
    return false;
  }

  const subject = `Thank you for contacting ${WEBSITE_NAME}`;
  const html = await getTemplate('user-confirmation', {
    name: escapeHtml(name),
    message: escapeHtml(message).replace(/\n/g, '<br>'),
    websiteName: WEBSITE_NAME,
  });
  const text = `
Thank you for your message

Dear ${name},

Thank you for contacting ${WEBSITE_NAME}. We have received your message and will get back to you as soon as possible.

Here's a copy of your message:

${message}

If you have any additional information to share, please don't hesitate to reply to this email.

Best regards,
${WEBSITE_NAME} Team

This is an automated message, please do not reply directly to this email.
  `;

  return sendEmail(email, subject, html, text, domain);
}

// Initialize the email system
export function initializeEmailSystem(): void {
  initializeTransporter();
}

// Test email configuration
export async function testEmailConfiguration(): Promise<boolean> {
  if (!transporter) {
    initializeTransporter();
  }

  try {
    await transporter.verify();
    return true;
  } catch (error) {
    console.error('Email configuration test failed:', error);
    return false;
  }
}
 