import { afterEach, describe, expect, it } from "vitest";
import nodemailer from "nodemailer";
import {
  MAIL_TEXT_ENCODING,
  buildVerifyLinkParts,
  resolveMailFrom,
} from "../../lib/server/mail";

const VERIFY_URL =
  "https://staging.communityrule.info/api/auth/magic-link/verify?token=5IdE_BHowaw-QJj7Rwue7CbB8wDXvYITvnxRb1FGqxA";

function decodeBase64Parts(raw: string): string {
  const blocks = [
    ...raw.matchAll(
      /Content-Transfer-Encoding:\s*base64\s*\r?\n\r?\n([A-Za-z0-9+/=\s]+)/gi,
    ),
  ];
  return blocks
    .map((match) =>
      Buffer.from(match[1].replace(/\s/g, ""), "base64").toString("utf8"),
    )
    .join("\n");
}

const MAIL_FROM_KEYS = ["SMTP_FROM", "CLOUDRON_MAIL_FROM"] as const;
const ORIGINAL_FROM = Object.fromEntries(
  MAIL_FROM_KEYS.map((key) => [key, process.env[key]]),
) as Record<(typeof MAIL_FROM_KEYS)[number], string | undefined>;

afterEach(() => {
  for (const key of MAIL_FROM_KEYS) {
    const original = ORIGINAL_FROM[key];
    if (original === undefined) delete process.env[key];
    else process.env[key] = original;
  }
});

describe("buildVerifyLinkParts", () => {
  it("puts the exact verify URL in text, href, and visible HTML link", () => {
    const { text, html } = buildVerifyLinkParts(
      VERIFY_URL,
      "Open this link to sign in (it expires in 60 minutes):",
      "If you did not request this, you can ignore this email.",
      "Sign in",
    );
    expect(text).toContain(`<${VERIFY_URL}>`);
    expect(html).toContain(`href="${VERIFY_URL}"`);
    expect(html).toContain(`>${VERIFY_URL}</a>`);
    expect(html).toContain(">Sign in</a>");
  });

  it("escapes HTML in the intro and href", () => {
    const { html } = buildVerifyLinkParts(
      'https://example.test/verify?token=a&b="c"',
      'View "Rule <beta>"',
      "Ignore if unexpected.",
      "Open",
    );
    expect(html).toContain("View &quot;Rule &lt;beta&gt;&quot;");
    expect(html).toContain(
      'href="https://example.test/verify?token=a&amp;b=&quot;c&quot;"',
    );
  });
});

describe("MIME encoding of verify-link mail", () => {
  it("uses base64 so the raw MIME never contains token=3D", async () => {
    const { text, html } = buildVerifyLinkParts(
      VERIFY_URL,
      "Open this link to sign in (it expires in 60 minutes):",
      "If you did not request this, you can ignore this email.",
      "Sign in",
    );
    const transporter = nodemailer.createTransport({
      streamTransport: true,
      buffer: true,
      newline: "unix",
    });
    const info = await transporter.sendMail({
      from: "Community Rule <staging.app@communityrule.info>",
      to: "member@example.com",
      subject: "Sign in to Community Rule",
      text,
      html,
      textEncoding: MAIL_TEXT_ENCODING,
    });
    const raw = Buffer.isBuffer(info.message)
      ? info.message.toString("utf8")
      : String(info.message);
    expect(raw).toMatch(/Content-Transfer-Encoding:\s*base64/i);
    expect(raw).not.toContain("token=3D");
    expect(raw).not.toMatch(/quoted-printable/i);
    const decoded = decodeBase64Parts(raw);
    expect(decoded).toContain(`href="${VERIFY_URL}"`);
    expect(decoded).toContain(VERIFY_URL);
  });
});

describe("resolveMailFrom", () => {
  it("prefers SMTP_FROM, then CLOUDRON_MAIL_FROM", () => {
    delete process.env.SMTP_FROM;
    delete process.env.CLOUDRON_MAIL_FROM;
    expect(resolveMailFrom()).toBe("noreply@localhost");

    process.env.CLOUDRON_MAIL_FROM = "staging.app@communityrule.info";
    expect(resolveMailFrom()).toBe("staging.app@communityrule.info");

    process.env.SMTP_FROM = "Community Rule <hello@communityrule.info>";
    expect(resolveMailFrom()).toBe(
      "Community Rule <hello@communityrule.info>",
    );
  });
});
