import nodemailer from "nodemailer";
import { logger } from "../logger";
import { getSmtpUrl } from "./env";

function escapeHtml(value: string): string {
  return value
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
}

export function resolveMailFrom(): string {
  return (
    process.env.SMTP_FROM?.trim() ||
    process.env.CLOUDRON_MAIL_FROM?.trim() ||
    "noreply@localhost"
  );
}

/** Avoid quoted-printable `token=3D` wrapping that some webmail clients do not decode. */
export const MAIL_TEXT_ENCODING = "base64" as const;

/** Plaintext + HTML for one-time verify URLs. */
export function buildVerifyLinkParts(
  verifyUrl: string,
  intro: string,
  outro: string,
  linkLabel: string,
): { text: string; html: string } {
  const href = escapeHtml(verifyUrl);
  const text = `${intro}\n\n<${verifyUrl}>\n\n${outro}`;
  const html =
    `<p>${escapeHtml(intro).replace(/\n/g, "<br />")}</p>` +
    `<p><a href="${href}">${escapeHtml(linkLabel)}</a></p>` +
    `<p><a href="${href}">${href}</a></p>` +
    `<p>${escapeHtml(outro)}</p>`;
  return { text, html };
}

async function sendHtmlMail(opts: {
  to: string;
  subject: string;
  text: string;
  html: string;
  from?: string;
  replyTo?: string;
  devLog: string;
}): Promise<void> {
  const smtpUrl = getSmtpUrl();

  if (!smtpUrl) {
    if (process.env.NODE_ENV === "development") {
      logger.info(opts.devLog);
      return;
    }
    throw new Error("CLOUDRON_MAIL_SMTP_* is not configured");
  }

  const transporter = nodemailer.createTransport(smtpUrl);
  await transporter.sendMail({
    from: opts.from ?? resolveMailFrom(),
    to: opts.to,
    subject: opts.subject,
    text: opts.text,
    html: opts.html,
    textEncoding: MAIL_TEXT_ENCODING,
    replyTo: opts.replyTo,
  });
}

export async function sendMagicLinkEmail(
  to: string,
  verifyUrl: string,
): Promise<void> {
  const { text, html } = buildVerifyLinkParts(
    verifyUrl,
    "Open this link to sign in (it expires in 60 minutes):",
    "If you did not request this, you can ignore this email.",
    "Sign in",
  );
  await sendHtmlMail({
    to,
    subject: "Sign in to Community Rule",
    text,
    html,
    devLog: `[dev] Magic link for ${to}: ${verifyUrl}`,
  });
}

/** Stakeholder invite after rule publish (one-time link, same dev/Mailhog pattern as magic link). */
export async function sendRuleStakeholderInviteEmail(
  to: string,
  verifyUrl: string,
  ruleTitle: string,
): Promise<void> {
  const { text, html } = buildVerifyLinkParts(
    verifyUrl,
    `You've been invited to view "${ruleTitle}" on Community Rule.\n\nOpen this link to create your account (or sign in) and open the rule. The link expires in 60 minutes and works once:`,
    "If you did not expect this, you can ignore this email.",
    "Open the rule",
  );
  await sendHtmlMail({
    to,
    subject: `You're invited to view a Community Rule: ${ruleTitle}`,
    text,
    html,
    devLog: `[dev] Rule stakeholder invite (${ruleTitle}) for ${to}: ${verifyUrl}`,
  });
}

/** Notify support/organizers when a visitor submits the Ask an organizer form. */
export async function sendOrganizerInquiryNotification(params: {
  /** Destination inbox (e.g. from ORGANIZER_INQUIRY_TO). */
  to: string;
  fromEmail: string;
  visitorEmail: string;
  message: string;
  requestId: string;
}): Promise<void> {
  const { to, fromEmail, visitorEmail, message, requestId } = params;
  const text = `Request ID: ${requestId}\nFrom: ${visitorEmail}\n\n${message}\n`;
  const html =
    `<p>Request ID: ${escapeHtml(requestId)}<br />From: ${escapeHtml(visitorEmail)}</p>` +
    `<pre>${escapeHtml(message)}</pre>`;
  await sendHtmlMail({
    to,
    from: fromEmail,
    replyTo: visitorEmail,
    subject: `Ask an organizer inquiry from ${visitorEmail}`,
    text,
    html,
    devLog: `[dev] Organizer inquiry (request ${requestId}) from ${visitorEmail} to ${to}:\n${message}`,
  });
}

export async function sendEmailChangeEmail(
  to: string,
  verifyUrl: string,
): Promise<void> {
  const { text, html } = buildVerifyLinkParts(
    verifyUrl,
    "You asked to change the email on your Community Rule account.\n\nOpen this link to confirm the new address (it expires in 60 minutes):",
    "If you did not request this change, you can ignore this email. Your current login is unchanged until you confirm.",
    "Confirm email",
  );
  await sendHtmlMail({
    to,
    subject: "Confirm your new Community Rule email",
    text,
    html,
    devLog: `[dev] Email change verify for ${to}: ${verifyUrl}`,
  });
}
