/**
 * email-guard — pure email normalization + throwaway/relay provider blocking.
 *
 * No DB, no React, no external dependency. All functions are pure.
 *
 * Goals:
 *  - Normalize emails so the same human can't trivially create multiple free
 *    accounts: plus-addressing (`+tag`) is stripped for ALL domains, and
 *    Gmail/googlemail additionally get dots stripped + googlemail → gmail
 *    canonicalization. Dots are NOT stripped on other providers (they stay
 *    meaningful there).
 *  - Reject disposable + relay/alias providers that defeat "one email = one user".
 */

const GMAIL_DOMAINS: ReadonlySet<string> = new Set([
  "gmail.com",
  "googlemail.com",
]);

/**
 * Curated relay/alias + disposable email domains to block.
 *
 * This list is intentionally focused, not exhaustive — extend it as new
 * abusive providers show up. Mainstream consumer providers (gmail.com,
 * outlook.com, proton.me, fastmail.com, yahoo.com, …) are deliberately NOT
 * listed: blocking them would lock out real users, and their plus-aliases
 * are neutralized by normalizeEmail.
 *
 * Exception — Apple (icloud.com / me.com / mac.com): Hide My Email generates
 * random @icloud.com addresses indistinguishable from real ones, so the whole
 * family is blocked. Product decision: Apple users must use another address.
 */
export const BLOCKED_EMAIL_DOMAINS: ReadonlySet<string> = new Set([
  // Relay / alias services (forward to a hidden real inbox)
  "privaterelay.appleid.com",
  "mozmail.com",
  "relay.firefox.com",
  "33mail.com",
  "duck.com",
  // Apple aliasing (Hide My Email — see header comment)
  "icloud.com",
  "me.com",
  "mac.com",
  // SimpleLogin family
  "simplelogin.com",
  "simplelogin.io",
  "slmail.me",
  "aleeas.com",
  "8alias.com",
  "dralias.com",
  "slmails.com",
  // addy.io / AnonAddy
  "addy.io",
  "anonaddy.com",
  "anonaddy.me",
  "mailer.me",
  // Forwarding / alias services (improvmx = its own domain only;
  // customer domain-forwarding can't be detected here)
  "forwardemail.net",
  "improvmx.com",
  "erine.email",
  // Disposable / throwaway inboxes
  "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",
  "tempr.email",
  "discard.email",
  "spamgourmet.com",
  "mytemp.email",
  "mail.tm",
  "inboxkitten.com",
  "emailondeck.com",
  "mohmal.com",
  "burnermail.io",
  "mailsac.com",
]);

/** French message returned by checkEmail when an address is rejected. */
export const BLOCKED_EMAIL_REASON =
  "Les adresses email jetables ou relais ne sont pas autorisées.";

/**
 * A "valid-looking" email: exactly one `@`, with a non-empty local part and a
 * domain that contains at least one dot and no whitespace. This is a
 * normalization gate, not RFC validation — better-auth does real validation.
 */
function splitEmail(email: string): { local: string; domain: string } | null {
  const at = email.lastIndexOf("@");
  if (at <= 0 || at === email.length - 1) return null;
  const local = email.slice(0, at);
  const domain = email.slice(at + 1);
  if (!local || !domain) return null;
  if (/\s/.test(email)) return null;
  if (!domain.includes(".")) return null;
  return { local, domain };
}

/**
 * Normalize an email address.
 *
 *  - Lowercases and trims.
 *  - Gmail / googlemail: strips ALL dots in the local part, drops everything
 *    from the first `+`, and canonicalizes googlemail.com → gmail.com.
 *  - Other domains: drops everything from the first `+` (plus-addressing) but
 *    keeps dots.
 *
 * If the input isn't a valid-looking email, returns the lowercased/trimmed
 * input unchanged (never throws).
 */
export function normalizeEmail(email: string): string {
  const cleaned = email.trim().toLowerCase();
  const parts = splitEmail(cleaned);
  if (!parts) return cleaned;

  let { local } = parts;
  const { domain } = parts;

  // Plus-addressing: drop the tag for every provider.
  const plus = local.indexOf("+");
  if (plus !== -1) local = local.slice(0, plus);

  if (GMAIL_DOMAINS.has(domain)) {
    local = local.replaceAll(".", "");
    return `${local}@gmail.com`;
  }

  return `${local}@${domain}`;
}

/** Extract the domain from a (possibly malformed) email, or null. */
function domainOf(email: string): string | null {
  const parts = splitEmail(email);
  return parts ? parts.domain : null;
}

/**
 * True if the (normalized) email's domain is in the blocklist OR is a subdomain
 * of a blocked domain (e.g. `anything.33mail.com`).
 */
export function isBlockedEmail(email: string): boolean {
  const domain = domainOf(normalizeEmail(email));
  if (!domain) return false;
  if (BLOCKED_EMAIL_DOMAINS.has(domain)) return true;
  for (const blocked of BLOCKED_EMAIL_DOMAINS) {
    if (domain.endsWith(`.${blocked}`)) return true;
  }
  return false;
}

/**
 * Normalize, then either accept (returning the normalized email) or reject
 * (returning a French reason).
 */
export function checkEmail(
  email: string,
): { ok: true; email: string } | { ok: false; reason: string } {
  const normalized = normalizeEmail(email);
  if (isBlockedEmail(normalized)) {
    return { ok: false, reason: BLOCKED_EMAIL_REASON };
  }
  return { ok: true, email: normalized };
}
