import { describe, it, expect, beforeEach } from "vitest";
import { InMemoryCampaignLogsRepository } from "@/modules/campaigns/repositories/in-memory-campaign-logs-repository";
import { InMemoryContactsRepository } from "@/modules/contacts/repositories/in-memory-contacts-repository";
import { InMemorySuppressedEmailsRepository } from "@/modules/suppression/repositories/in-memory-suppressed-emails-repository";
import { InMemoryEmailEventsRepository } from "../repositories/in-memory-email-events-repository";
import { InMemorySesSuppressionProvider } from "../providers/in-memory-ses-suppression-provider";
import { HandleSesNotificationUseCase } from "./handle-ses-notification-use-case";

let campaignLogsRepository: InMemoryCampaignLogsRepository;
let contactsRepository: InMemoryContactsRepository;
let suppressedEmailsRepository: InMemorySuppressedEmailsRepository;
let emailEventsRepository: InMemoryEmailEventsRepository;
let sesSuppressionProvider: InMemorySesSuppressionProvider;
let sut: HandleSesNotificationUseCase;

describe("Handle SES Notification Use Case", () => {
  beforeEach(() => {
    campaignLogsRepository = new InMemoryCampaignLogsRepository();
    contactsRepository = new InMemoryContactsRepository();
    suppressedEmailsRepository = new InMemorySuppressedEmailsRepository();
    emailEventsRepository = new InMemoryEmailEventsRepository();
    sesSuppressionProvider = new InMemorySesSuppressionProvider();
    sut = new HandleSesNotificationUseCase(
      campaignLogsRepository,
      contactsRepository,
      suppressedEmailsRepository,
      emailEventsRepository,
      sesSuppressionProvider
    );
  });

  it("should suppress the recipient and mark the log failed on a permanent bounce", async () => {
    await contactsRepository.createMany(["bounced@example.com"]);
    const [contact] = await contactsRepository.findAll();
    const log = await campaignLogsRepository.upsertPending("campaign-1", contact.id);
    await campaignLogsRepository.updateMessageId(log.id, "msg-1");
    campaignLogsRepository.items[0].contact = contact;

    await sut.execute({
      rawMessage: {
        eventType: "Bounce",
        mail: { messageId: "msg-1", destination: ["bounced@example.com"] },
        bounce: {
          bounceType: "Permanent",
          bouncedRecipients: [{ emailAddress: "bounced@example.com" }],
        },
      },
    });

    expect(await suppressedEmailsRepository.isSuppressed("bounced@example.com")).toBe(true);
    expect(sesSuppressionProvider.suppressed.get("bounced@example.com")).toEqual("BOUNCE");
    const updatedContact = await contactsRepository.findAll();
    expect(updatedContact[0].status).toEqual("BOUNCED");
    expect(emailEventsRepository.items).toHaveLength(1);
  });

  it("should suppress the recipient on a complaint", async () => {
    await contactsRepository.createMany(["complainer@example.com"]);
    const [contact] = await contactsRepository.findAll();
    const log = await campaignLogsRepository.upsertPending("campaign-1", contact.id);
    await campaignLogsRepository.updateMessageId(log.id, "msg-2");
    campaignLogsRepository.items[0].contact = contact;

    await sut.execute({
      rawMessage: {
        eventType: "Complaint",
        mail: { messageId: "msg-2", destination: ["complainer@example.com"] },
        complaint: { complainedRecipients: [{ emailAddress: "complainer@example.com" }] },
      },
    });

    expect(await suppressedEmailsRepository.isSuppressed("complainer@example.com")).toBe(true);
    expect(sesSuppressionProvider.suppressed.get("complainer@example.com")).toEqual("COMPLAINT");
  });

  it("should record delivery/open/click events without suppressing anyone", async () => {
    await sut.execute({
      rawMessage: { eventType: "Delivery", mail: { messageId: "msg-3" } },
    });

    expect(emailEventsRepository.items).toHaveLength(1);
    expect(emailEventsRepository.items[0].type).toEqual("DELIVERY");
  });

  it("should ignore malformed payloads without throwing", async () => {
    await expect(sut.execute({ rawMessage: { nonsense: true } })).resolves.toBeUndefined();
    expect(emailEventsRepository.items).toHaveLength(0);
  });
});
