// Template DB access, seeding, and the core template/custom send + logging logic.

import { db, dbGet, emailTemplates, emailLogs } from '../../db/index.js';
import { eq } from 'drizzle-orm';
import { getNow, generateId } from '../utils.js';
import { replaceTemplateVariables, wrapInBaseTemplate, defaultTemplates } from '../emailTemplates.js';
import { sendEmail } from './transport.js';
import { getCommonVariables } from './formatting.js';

/**
 * Get a template by slug
 */
export async function getTemplate(slug: string): Promise<any | null> {
  const template = await dbGet(
    (db as any)
      .select()
      .from(emailTemplates)
      .where(eq((emailTemplates as any).slug, slug))
  );

  return template || null;
}

/**
 * Seed default templates if they don't exist, and update system templates with latest content
 */
export async function seedDefaultTemplates(): Promise<void> {
  console.log('[Email] Checking for default templates...');

  for (const template of defaultTemplates) {
    const existing = await getTemplate(template.slug);
    const now = getNow();

    if (!existing) {
      console.log(`[Email] Creating template: ${template.name}`);

      await (db as any).insert(emailTemplates).values({
        id: generateId(),
        name: template.name,
        slug: template.slug,
        subject: template.subject,
        subjectEs: template.subjectEs,
        bodyHtml: template.bodyHtml,
        bodyHtmlEs: template.bodyHtmlEs,
        bodyText: template.bodyText,
        bodyTextEs: template.bodyTextEs,
        description: template.description,
        variables: JSON.stringify(template.variables),
        isSystem: template.isSystem ? 1 : 0,
        isActive: 1,
        createdAt: now,
        updatedAt: now,
      });
    } else if (existing.isSystem) {
      // Update system templates with latest content from defaults
      console.log(`[Email] Updating system template: ${template.name}`);

      await (db as any)
        .update(emailTemplates)
        .set({
          subject: template.subject,
          subjectEs: template.subjectEs,
          bodyHtml: template.bodyHtml,
          bodyHtmlEs: template.bodyHtmlEs,
          bodyText: template.bodyText,
          bodyTextEs: template.bodyTextEs,
          description: template.description,
          variables: JSON.stringify(template.variables),
          updatedAt: now,
        })
        .where(eq((emailTemplates as any).slug, template.slug));
    }
  }

  console.log('[Email] Default templates check complete');
}

/**
 * Send an email using a template
 */
export async function sendTemplateEmail(params: {
  templateSlug: string;
  to: string;
  toName?: string;
  variables: Record<string, any>;
  locale?: string;
  eventId?: string;
  sentBy?: string;
}): Promise<{ success: boolean; logId?: string; error?: string }> {
  const { templateSlug, to, toName, variables, locale = 'en', eventId, sentBy } = params;

  // Get template
  const template = await getTemplate(templateSlug);
  if (!template) {
    return { success: false, error: `Template "${templateSlug}" not found` };
  }

  // Build variables
  const allVariables = {
    ...getCommonVariables(),
    lang: locale,
    ...variables,
  };

  // Get localized content
  const subject = locale === 'es' && template.subjectEs
    ? template.subjectEs
    : template.subject;
  const bodyHtml = locale === 'es' && template.bodyHtmlEs
    ? template.bodyHtmlEs
    : template.bodyHtml;
  const bodyText = locale === 'es' && template.bodyTextEs
    ? template.bodyTextEs
    : template.bodyText;

  // Replace variables
  const finalSubject = replaceTemplateVariables(subject, allVariables);
  const finalBodyContent = replaceTemplateVariables(bodyHtml, allVariables, true);
  const finalBodyHtml = wrapInBaseTemplate(finalBodyContent, { ...allVariables, subject: finalSubject });
  const finalBodyText = bodyText ? replaceTemplateVariables(bodyText, allVariables) : undefined;

  // Create log entry
  const logId = generateId();
  const now = getNow();

  await (db as any).insert(emailLogs).values({
    id: logId,
    templateId: template.id,
    eventId: eventId || null,
    recipientEmail: to,
    recipientName: toName || null,
    subject: finalSubject,
    bodyHtml: finalBodyHtml,
    status: 'pending',
    sentBy: sentBy || null,
    createdAt: now,
  });

  // Send email
  const result = await sendEmail({
    to,
    subject: finalSubject,
    html: finalBodyHtml,
    text: finalBodyText,
  });

  // Update log with result
  if (result.success) {
    await (db as any)
      .update(emailLogs)
      .set({
        status: 'sent',
        sentAt: getNow(),
      })
      .where(eq((emailLogs as any).id, logId));
  } else {
    await (db as any)
      .update(emailLogs)
      .set({
        status: 'failed',
        errorMessage: result.error,
      })
      .where(eq((emailLogs as any).id, logId));
  }

  return {
    success: result.success,
    logId,
    error: result.error
  };
}

/**
 * Send a custom email (not from template)
 */
export async function sendCustomEmail(params: {
  to: string;
  toName?: string;
  subject: string;
  bodyHtml: string;
  bodyText?: string;
  replyTo?: string;
  eventId?: string;
  sentBy?: string | null;
}): Promise<{ success: boolean; logId?: string; error?: string }> {
  const { to: rawTo, toName, subject: rawSubject, bodyHtml, bodyText, replyTo: rawReplyTo, eventId, sentBy = null } = params;

  // Strip CR/LF from header-bound values to prevent email header injection
  // (e.g. an attacker-supplied subject/replyTo smuggling extra headers/recipients).
  const stripHeader = (v?: string) => (v ? v.replace(/[\r\n]+/g, ' ').trim() : v);
  const to = stripHeader(rawTo) as string;
  const subject = stripHeader(rawSubject) as string;
  const replyTo = stripHeader(rawReplyTo);

  const allVariables = {
    ...getCommonVariables(),
    subject,
  };

  const finalBodyHtml = wrapInBaseTemplate(bodyHtml, allVariables);

  // Create log entry
  const logId = generateId();
  const now = getNow();

  await (db as any).insert(emailLogs).values({
    id: logId,
    templateId: null,
    eventId: eventId || null,
    recipientEmail: to,
    recipientName: toName || null,
    subject,
    bodyHtml: finalBodyHtml,
    status: 'pending',
    sentBy: sentBy || null,
    createdAt: now,
  });

  // Send email
  const result = await sendEmail({
    to,
    subject,
    html: finalBodyHtml,
    text: bodyText,
    replyTo,
  });

  // Update log
  if (result.success) {
    await (db as any)
      .update(emailLogs)
      .set({
        status: 'sent',
        sentAt: getNow(),
      })
      .where(eq((emailLogs as any).id, logId));
  } else {
    await (db as any)
      .update(emailLogs)
      .set({
        status: 'failed',
        errorMessage: result.error,
      })
      .where(eq((emailLogs as any).id, logId));
  }

  return {
    success: result.success,
    logId,
    error: result.error
  };
}

/**
 * Resend an email from an existing log entry
 */
export async function resendFromLog(logId: string): Promise<{ success: boolean; error?: string }> {
  const log = await dbGet<any>(
    (db as any).select().from(emailLogs).where(eq((emailLogs as any).id, logId))
  );

  if (!log) {
    return { success: false, error: 'Email log not found' };
  }

  if (!log.bodyHtml || !log.subject || !log.recipientEmail) {
    return { success: false, error: 'Email log missing required data to resend' };
  }

  const result = await sendEmail({
    to: log.recipientEmail,
    subject: log.subject,
    html: log.bodyHtml,
    text: undefined,
  });

  const now = getNow();
  const currentResendAttempts = (log.resendAttempts ?? 0) + 1;

  if (result.success) {
    await (db as any)
      .update(emailLogs)
      .set({
        status: 'sent',
        sentAt: now,
        errorMessage: null,
        resendAttempts: currentResendAttempts,
        lastResentAt: now,
      })
      .where(eq((emailLogs as any).id, logId));
  } else {
    await (db as any)
      .update(emailLogs)
      .set({
        status: 'failed',
        errorMessage: result.error,
        resendAttempts: currentResendAttempts,
        lastResentAt: now,
      })
      .where(eq((emailLogs as any).id, logId));
  }

  return {
    success: result.success,
    error: result.error,
  };
}
