/**
 * Marketing email send wrapper.
 *
 * Thin shim over `email/transport.sendRaw` that:
 *   - mints a per-customer HMAC unsubscribe token
 *   - injects List-Unsubscribe + List-Unsubscribe-Post (RFC 8058) headers
 *     so Gmail/Apple Mail surface a one-click unsubscribe button
 *   - appends a visible footer link as a backup for clients that ignore
 *     the header (e.g. Outlook desktop)
 *   - classifies SMTP errors so the dispatcher can distinguish permanent
 *     failures (bad recipient, auth broken) from transient ones (connection
 *     timeout, server-side rate limit) and act accordingly.
 */

import { sendRaw } from '@server/services/email/transport';
import { mintUnsubscribeToken } from './unsubscribe-token';

interface SendCampaignEmailInput {
  to: string;
  subject: string;
  bodyHtml: string;
  restaurantId: string;
  branchId: string | null;
  customerId: string | null;
}

function buildPublicBase(): string {
  const env =
    process.env.NEXT_PUBLIC_APP_URL ||
    process.env.APP_URL ||
    process.env.PUBLIC_URL ||
    process.env.REPLIT_DEV_DOMAIN ||
    'http://localhost:5000';
  return env.startsWith('http') ? env.replace(/\/+$/, '') : `https://${env.replace(/\/+$/, '')}`;
}

function appendUnsubscribeFooter(html: string, unsubUrl: string): string {
  const footer = `
<hr style="margin-top:32px;border:none;border-top:1px solid #e5e7eb"/>
<p style="font-size:12px;color:#6b7280;text-align:center;margin-top:16px;font-family:Arial,Helvetica,sans-serif">
  Don't want to receive these emails?
  <a href="${unsubUrl}" style="color:#6b7280;text-decoration:underline">Unsubscribe</a>.
</p>`.trim();
  if (/<\/body\s*>/i.test(html)) {
    return html.replace(/<\/body\s*>/i, `${footer}</body>`);
  }
  return `${html}\n${footer}`;
}

/**
 * Classify an SMTP / transport error so the dispatcher knows whether to
 * retry or permanently skip the delivery.
 *
 * permanent = true  → mark delivery 'skipped', never retry.
 * permanent = false → mark delivery 'failed', retry with exponential backoff.
 */
function classifySmtpError(err: unknown): { error: string; permanent: boolean } {
  const e = err as { code?: string; responseCode?: number; response?: string; message?: string };
  const msg = (e.message ?? String(err)).trim();
  const code = (e.code ?? '').toUpperCase();
  const rc = typeof e.responseCode === 'number' ? e.responseCode : 0;
  const resp = (e.response ?? '').toLowerCase();

  // ── No SMTP configured ─────────────────────────────────────────────────
  // sendRaw returns null when no transport is set up; this wrapper converts
  // that to a permanent error so the operator can see it in the report.
  if (!msg || msg.toLowerCase().includes('no smtp')) {
    return {
      error: 'No SMTP transport configured for this restaurant. Configure SMTP in Branch Settings.',
      permanent: true,
    };
  }

  // ── SMTP authentication failure (permanent) ────────────────────────────
  // The credentials are wrong — retrying the same delivery won't help.
  // Operators need to fix their SMTP settings.
  if (
    code === 'EAUTH' ||
    rc === 535 || rc === 534 || rc === 530 ||
    resp.includes('authentication') ||
    resp.includes('auth') && (resp.includes('failed') || resp.includes('invalid'))
  ) {
    return {
      error: `SMTP authentication failed — check your SMTP credentials in Branch Settings. (${msg})`,
      permanent: true,
    };
  }

  // ── Permanent recipient-level rejections (5xx codes) ──────────────────
  // The remote mail server definitively rejected this address.
  // 550: Mailbox not found / does not exist
  // 551: User not local; try forwarding
  // 553: Mailbox name not allowed (syntax/policy)
  // 554: Transaction failed / message rejected as spam policy
  if (rc === 550 || rc === 551 || rc === 553 || rc === 554) {
    return {
      error: `Recipient address rejected by remote server (SMTP ${rc}): ${msg}`,
      permanent: true,
    };
  }

  // ── Transient errors — safe to retry ──────────────────────────────────
  // 421: Service temporarily unavailable
  // 450/452: Mailbox temporarily unavailable / server busy
  // ECONNREFUSED / ETIMEDOUT / ENOTFOUND: network / DNS issue
  return { error: msg, permanent: false };
}

export async function sendCampaignEmail(
  input: SendCampaignEmailInput
): Promise<{ messageId: string | null; error?: string; permanent?: boolean }> {
  // Reject obvious garbage early — full RFC-5322 is overkill but we catch
  // whitespace, missing TLDs, and angle-bracket / comma injection here.
  // `permanent: true` tells the dispatcher to mark the row 'skipped' so the
  // operator sees the bad address in the report without burning retry slots.
  const EMAIL_RE = /^[^\s,;<>"']+@[^\s,;<>"'@]+\.[^\s,;<>"'@]{2,}$/;
  if (!input.to || !EMAIL_RE.test(input.to.trim())) {
    return { messageId: null, error: `Invalid email address: "${input.to}"`, permanent: true };
  }

  let headers: Record<string, string> | undefined;
  let bodyHtml = input.bodyHtml;

  if (input.customerId) {
    const token = mintUnsubscribeToken(input.restaurantId, input.customerId);
    const unsubUrl = `${buildPublicBase()}/api/public/unsubscribe/${encodeURIComponent(token)}`;
    headers = {
      'List-Unsubscribe': `<${unsubUrl}>`,
      'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
    };
    bodyHtml = appendUnsubscribeFooter(bodyHtml, unsubUrl);
  }

  try {
    const result = await sendRaw(
      input.to, input.subject, bodyHtml, input.branchId, input.restaurantId,
      undefined,
      headers ? { headers } : {}
    );
    if (!result) {
      // sendRaw returns null/undefined when no SMTP transport is configured.
      return {
        messageId: null,
        error: 'No SMTP transport configured for this restaurant. Configure SMTP in Branch Settings.',
        permanent: true,
      };
    }
    return { messageId: result.messageId };
  } catch (err) {
    const { error, permanent } = classifySmtpError(err);
    return { messageId: null, error, permanent };
  }
}
