/**
 * Tests: Layer 2.6 — User-Custom-Display-Name-Matching
 *
 * Testet:
 *   - classifyMail() blockt wenn customDisplayNames-Pattern im Display-Name enthalten
 *   - Case-insensitive Match (Brand-Rotation: EXTRASPIN / extraspin / ExtraSpin)
 *   - Substring-Match ("EXTRASPIN Casino" wird von Pattern "EXTRASPIN" erfasst)
 *   - Kein Block wenn Display-Name nicht matcht
 *   - Kein Block wenn senderName null
 *   - type='mail_domain' matcht weiterhin via getBlocklistedDomainsSet (bestehend)
 *   - Shared Slot-Count: 3 web + 2 mail_domain + 1 mail_display_name → count=6
 *
 * DSGVO: keine PII-Mails in Tests. Synthetic Brand-Namen (EXTRASPIN, CASINOX).
 */
import { describe, it, expect, vi } from "vitest";

vi.mock("../../server/utils/gambling-keywords.mjs", () => ({
  GAMBLING_KEYWORDS: [
    "casino", "bet365", "bwin", "tipico", "jackpot", "freispiel",
    "slots", "roulette", "wette", "stake", "spinz", "casinoly",
  ],
  GAMBLING_WHITELIST: [
    "wettervorhersage",
    "wetter",
    "wetterbericht",
    "wettkampf",
    "wettbewerb",
  ],
}));

import { classifyMail } from "../../server/utils/mail-classifier";

// ─── Layer 2.6: Display-Name-Match ──────────────────────────────────────────

describe("classifyMail() — Layer 2.6 Custom Display-Name-Match", () => {
  const emptyDomainSet = new Set<string>();

  it("EXTRASPIN matcht exakt als Substring → BLOCK (custom-display-name)", async () => {
    const result = await classifyMail({
      mail: {
        senderEmail: "noreply@em123.delivery.net",
        senderName: "EXTRASPIN",
        subject: "Dein Bonus wartet",
      },
      blockedDomainSet: emptyDomainSet,
      customDisplayNames: ["EXTRASPIN"],
    });

    expect(result.action).toBe("blocked");
    expect(result.triggerSource).toBe("custom-display-name");
    expect(result.score).toBe(100);
  });

  it("EXTRASPIN matcht 'EXTRASPIN Casino' als Substring → BLOCK", async () => {
    const result = await classifyMail({
      mail: {
        senderEmail: "noreply@em456.relay.net",
        senderName: "EXTRASPIN Casino",
        subject: "Exklusives Angebot für dich",
      },
      blockedDomainSet: emptyDomainSet,
      customDisplayNames: ["EXTRASPIN"],
    });

    expect(result.action).toBe("blocked");
    expect(result.triggerSource).toBe("custom-display-name");
  });

  it("EXTRASPIN matcht 'ExtraSpin Bonus' case-insensitiv → BLOCK", async () => {
    const result = await classifyMail({
      mail: {
        senderEmail: "noreply@em789.relay.net",
        senderName: "ExtraSpin Bonus",
        subject: "Willkommensbonus",
      },
      blockedDomainSet: emptyDomainSet,
      customDisplayNames: ["EXTRASPIN"],
    });

    expect(result.action).toBe("blocked");
    expect(result.triggerSource).toBe("custom-display-name");
  });

  it("extraspin (lowercase pattern) matcht 'EXTRASPIN Casino' case-insensitiv → BLOCK", async () => {
    const result = await classifyMail({
      mail: {
        senderEmail: "info@em.relay.net",
        senderName: "EXTRASPIN Casino",
        subject: "Willkommen",
      },
      blockedDomainSet: emptyDomainSet,
      customDisplayNames: ["extraspin"],
    });

    expect(result.action).toBe("blocked");
    expect(result.triggerSource).toBe("custom-display-name");
  });

  it("unrelated Display-Name 'Amazon' matcht nicht → PASS (kein Block)", async () => {
    const result = await classifyMail({
      mail: {
        senderEmail: "no-reply@amazon.de",
        senderName: "Amazon",
        subject: "Deine Bestellung wurde versandt",
      },
      blockedDomainSet: emptyDomainSet,
      customDisplayNames: ["EXTRASPIN"],
    });

    expect(result.action).toBe("passed");
    expect(result.triggerSource).not.toBe("custom-display-name");
  });

  it("senderName ist null → kein Layer-2.6-Block (kein Crash)", async () => {
    const result = await classifyMail({
      mail: {
        senderEmail: "info@some-relay.net",
        senderName: null,
        subject: "Test",
      },
      blockedDomainSet: emptyDomainSet,
      customDisplayNames: ["EXTRASPIN"],
    });

    // Kein Block durch Layer 2.6 — senderName=null, kein Match möglich
    expect(result.triggerSource).not.toBe("custom-display-name");
  });

  it("leere customDisplayNames → kein Layer-2.6-Block", async () => {
    const result = await classifyMail({
      mail: {
        senderEmail: "info@some-relay.net",
        senderName: "EXTRASPIN Casino",
        subject: "Test",
      },
      blockedDomainSet: emptyDomainSet,
      customDisplayNames: [],
    });

    expect(result.triggerSource).not.toBe("custom-display-name");
  });

  it("customDisplayNames fehlt (undefined) → kein Crash, kein Layer-2.6-Block", async () => {
    const result = await classifyMail({
      mail: {
        senderEmail: "info@some-relay.net",
        senderName: "EXTRASPIN Casino",
        subject: "Test",
      },
      blockedDomainSet: emptyDomainSet,
      // customDisplayNames nicht übergeben → optional, default undefined
    });

    expect(result.triggerSource).not.toBe("custom-display-name");
  });

  it("mehrere Patterns — zweites Pattern 'CASINOX' matcht → BLOCK", async () => {
    const result = await classifyMail({
      mail: {
        senderEmail: "noreply@em.relay.net",
        senderName: "CASINOX VIP",
        subject: "VIP-Angebot",
      },
      blockedDomainSet: emptyDomainSet,
      customDisplayNames: ["EXTRASPIN", "CASINOX"],
    });

    expect(result.action).toBe("blocked");
    expect(result.triggerSource).toBe("custom-display-name");
  });
});

// ─── Bestehende Domain-Types bleiben unverändert ──────────────────────────────

describe("classifyMail() — type='mail_domain' via blockedDomainSet (bestehend)", () => {
  it("mail_domain-Eintrag in blockedDomainSet → Layer-2-Block (domain)", async () => {
    // type='mail_domain' landet via getBlocklistedDomainsSet in blockedDomainSet —
    // Blocking-Logik ist identisch zu type='web'.
    const domainSet = new Set(["casinox.com"]);

    const result = await classifyMail({
      mail: {
        senderEmail: "promo@casinox.com",
        senderName: "CasinoX",
        subject: "Dein Bonus",
      },
      blockedDomainSet: domainSet,
      customDisplayNames: [],
    });

    expect(result.action).toBe("blocked");
    expect(result.triggerSource).toBe("domain");
    expect(result.features.domainBlocked).toBe(true);
  });
});

// ─── Separate Slot-Buckets (seit plan-limits-Refactor) ───────────────────────

describe("Separate Slot-Buckets — web vs. mail (Dokumentations-Test ohne DB)", () => {
  it("web-Slot und mail-Slot sind UNABHÄNGIG — 5 web voll blockiert nicht mail-Bucket", () => {
    // countActiveCustomDomainsSplit() gibt { web, mail } zurück.
    // mail-Bucket = mail_domain + mail_display_name kombiniert.
    // web-Bucket und mail-Bucket sind vollständig getrennt.
    //
    // Beispiel: 5 web belegt + 2 mail belegt → web-Limit erreicht, mail-Limit NICHT.
    // POST new mail → sollte 200 zurückgeben (mail-Slot frei).
    // POST new web  → sollte 403 WEB_LIMIT_REACHED zurückgeben.
    //
    // Detaillierte Logik-Tests: tests/custom-domains/plan-limits.test.ts
    expect(true).toBe(true); // Dokumentiert Semantik-Änderung von Shared→Separate
  });
});
