import { describe, expect, test } from "bun:test";
import {
  BLOCKED_EMAIL_DOMAINS,
  checkEmail,
  isBlockedEmail,
  normalizeEmail,
} from "./email-guard";

describe("normalizeEmail", () => {
  test("lowercases and trims", () => {
    expect(normalizeEmail("  Foo@Example.com  ")).toBe("foo@example.com");
  });

  test("gmail: strips dots and plus-tag", () => {
    expect(normalizeEmail("J.o.h.n+promo@gmail.com")).toBe("john@gmail.com");
  });

  test("gmail: strips dots even without plus", () => {
    expect(normalizeEmail("a.b.c@gmail.com")).toBe("abc@gmail.com");
  });

  test("googlemail canonicalizes to gmail (with dot/plus rules)", () => {
    expect(normalizeEmail("John.Doe+x@googlemail.com")).toBe(
      "johndoe@gmail.com",
    );
  });

  test("non-gmail: strips plus-tag but keeps dots", () => {
    expect(normalizeEmail("jane.doe+x@outlook.com")).toBe(
      "jane.doe@outlook.com",
    );
    expect(normalizeEmail("a+b@outlook.com")).toBe("a@outlook.com");
    expect(normalizeEmail("x+y@proton.me")).toBe("x@proton.me");
  });

  test("non-gmail without plus is unchanged (besides case) — dots preserved", () => {
    expect(normalizeEmail("Jane.Doe@Outlook.com")).toBe("jane.doe@outlook.com");
    expect(normalizeEmail("j.doe@outlook.com")).toBe("j.doe@outlook.com");
  });

  test("malformed input does not throw, returns trimmed/lowercased", () => {
    expect(() => normalizeEmail("not-an-email")).not.toThrow();
    expect(normalizeEmail("  NOT-an-email  ")).toBe("not-an-email");
    expect(normalizeEmail("a@@b")).toBe("a@@b");
    expect(normalizeEmail("@gmail.com")).toBe("@gmail.com");
    expect(normalizeEmail("foo@nodot")).toBe("foo@nodot");
    expect(normalizeEmail("")).toBe("");
  });
});

describe("BLOCKED_EMAIL_DOMAINS", () => {
  test("includes required relay + disposable domains", () => {
    for (const d of [
      "privaterelay.appleid.com",
      "mozmail.com",
      "relay.firefox.com",
      "33mail.com",
      "duck.com",
      "mailinator.com",
      "guerrillamail.com",
      "10minutemail.com",
      "tempmail.com",
      "temp-mail.org",
      "yopmail.com",
      "getnada.com",
      "trashmail.com",
      "sharklasers.com",
      "maildrop.cc",
      "dispostable.com",
      "fakeinbox.com",
      "throwawaymail.com",
    ]) {
      expect(BLOCKED_EMAIL_DOMAINS.has(d)).toBe(true);
    }
  });

  test("includes Apple aliasing domains (Hide My Email)", () => {
    for (const d of ["icloud.com", "me.com", "mac.com"]) {
      expect(BLOCKED_EMAIL_DOMAINS.has(d)).toBe(true);
    }
  });

  test("includes SimpleLogin + addy.io families and forwarding services", () => {
    for (const d of [
      "simplelogin.com",
      "simplelogin.io",
      "slmail.me",
      "aleeas.com",
      "8alias.com",
      "dralias.com",
      "slmails.com",
      "addy.io",
      "anonaddy.com",
      "anonaddy.me",
      "mailer.me",
      "forwardemail.net",
      "improvmx.com",
      "erine.email",
    ]) {
      expect(BLOCKED_EMAIL_DOMAINS.has(d)).toBe(true);
    }
  });

  test("includes newly added disposables", () => {
    for (const d of [
      "tempr.email",
      "discard.email",
      "spamgourmet.com",
      "mytemp.email",
      "mail.tm",
      "inboxkitten.com",
      "emailondeck.com",
      "mohmal.com",
      "burnermail.io",
      "mailsac.com",
    ]) {
      expect(BLOCKED_EMAIL_DOMAINS.has(d)).toBe(true);
    }
  });

  test("does NOT include mainstream providers", () => {
    for (const d of [
      "gmail.com",
      "outlook.com",
      "proton.me",
      "fastmail.com",
      "yahoo.com",
    ]) {
      expect(BLOCKED_EMAIL_DOMAINS.has(d)).toBe(false);
    }
  });
});

describe("isBlockedEmail", () => {
  test("blocks relay providers", () => {
    expect(isBlockedEmail("x@privaterelay.appleid.com")).toBe(true);
    expect(isBlockedEmail("x@mozmail.com")).toBe(true);
  });

  test("blocks subdomains of blocked domains", () => {
    expect(isBlockedEmail("foo@user.33mail.com")).toBe(true);
  });

  test("does not block mainstream providers", () => {
    expect(isBlockedEmail("x@gmail.com")).toBe(false);
    expect(isBlockedEmail("x@outlook.com")).toBe(false);
    expect(isBlockedEmail("x@proton.me")).toBe(false);
    expect(isBlockedEmail("x@fastmail.com")).toBe(false);
    expect(isBlockedEmail("x@yahoo.com")).toBe(false);
    expect(isBlockedEmail("x@mon-entreprise.fr")).toBe(false);
  });

  test("does not block a non-blocked domain that merely contains a blocked name", () => {
    expect(isBlockedEmail("x@not33mail.com")).toBe(false);
    expect(isBlockedEmail("x@duck.com.evil.com")).toBe(false);
  });

  test("malformed input is not blocked and does not throw", () => {
    expect(() => isBlockedEmail("garbage")).not.toThrow();
    expect(isBlockedEmail("garbage")).toBe(false);
  });
});

describe("checkEmail", () => {
  test("returns normalized email on ok", () => {
    expect(checkEmail("J.o.h.n+promo@gmail.com")).toEqual({
      ok: true,
      email: "john@gmail.com",
    });
    expect(checkEmail("jane.doe+x@outlook.com")).toEqual({
      ok: true,
      email: "jane.doe@outlook.com",
    });
  });

  test("returns a French reason when blocked", () => {
    const res = checkEmail("x@privaterelay.appleid.com");
    expect(res.ok).toBe(false);
    if (!res.ok) {
      expect(res.reason).toBe(
        "Les adresses email jetables ou relais ne sont pas autorisées.",
      );
    }
  });

  test("blocks subdomain of a blocked domain", () => {
    const res = checkEmail("foo@user.33mail.com");
    expect(res.ok).toBe(false);
  });

  test("blocks Apple aliasing domains (icloud/me/mac)", () => {
    expect(checkEmail("real.user@icloud.com").ok).toBe(false);
    expect(checkEmail("x@me.com").ok).toBe(false);
    expect(checkEmail("x@mac.com").ok).toBe(false);
  });

  test("blocks SimpleLogin, addy.io and new disposables", () => {
    expect(checkEmail("x@simplelogin.io").ok).toBe(false);
    expect(checkEmail("x@slmail.me").ok).toBe(false);
    expect(checkEmail("x@addy.io").ok).toBe(false);
    expect(checkEmail("x@anonaddy.me").ok).toBe(false);
    expect(checkEmail("x@mail.tm").ok).toBe(false);
    expect(checkEmail("x@mailsac.com").ok).toBe(false);
  });

  test("mainstream providers still pass, plus-tag stripped", () => {
    expect(checkEmail("a+b@outlook.com")).toEqual({
      ok: true,
      email: "a@outlook.com",
    });
    expect(checkEmail("x+y@proton.me")).toEqual({
      ok: true,
      email: "x@proton.me",
    });
    expect(checkEmail("x@fastmail.com").ok).toBe(true);
    expect(checkEmail("x@yahoo.com").ok).toBe(true);
    expect(checkEmail("contact+seo@mon-entreprise.fr")).toEqual({
      ok: true,
      email: "contact@mon-entreprise.fr",
    });
  });
});
