import { describe, it, expect, beforeEach } from "vitest";
import { ContactStatus } from "@prisma/client";
import { InMemoryContactsRepository } from "../repositories/in-memory-contacts-repository";
import { InMemoryCampaignLogsRepository } from "@/modules/campaigns/repositories/in-memory-campaign-logs-repository";
import { InMemoryEmailEventsRepository } from "@/modules/ses-webhooks/repositories/in-memory-email-events-repository";
import { GetContactEmailHistoryUseCase } from "./get-contact-email-history-use-case";
import { AppError } from "@/core/errors/app-error";

let contactsRepository: InMemoryContactsRepository;
let campaignLogsRepository: InMemoryCampaignLogsRepository;
let emailEventsRepository: InMemoryEmailEventsRepository;
let sut: GetContactEmailHistoryUseCase;

function makeCampaign(id: string, subject: string) {
  return {
    id,
    title: subject,
    subject,
    htmlBody: `<p>Corpo de ${subject}</p>`,
    status: "SENT" as const,
    senderId: "sender-1",
    contactListId: null,
    createdById: "user-1",
    createdAt: new Date(),
    updatedAt: new Date(),
    sender: { name: "Remetente", fromEmail: "remetente@example.com" },
  };
}

describe("Get Contact Email History Use Case", () => {
  beforeEach(() => {
    contactsRepository = new InMemoryContactsRepository();
    campaignLogsRepository = new InMemoryCampaignLogsRepository();
    emailEventsRepository = new InMemoryEmailEventsRepository();
    sut = new GetContactEmailHistoryUseCase(contactsRepository, campaignLogsRepository, emailEventsRepository);

    contactsRepository.items.push({
      id: "contact-1",
      email: "contact@example.com",
      name: null,
      lastName: null,
      phone: null,
      bairro: null,
      cidade: null,
      uf: null,
      idioma: null,
      empresa: null,
      cep: null,
      codigoEstabelecimento: null,
      nomeEstabelecimento: null,
      cdate: null,
      tags: [],
      source: "MANUAL",
      status: ContactStatus.ACTIVE,
      createdAt: new Date(),
      updatedAt: new Date(),
    });
  });

  it("throws AppError.notFound for an unknown contact", async () => {
    await expect(
      sut.execute({ contactId: "does-not-exist", page: 1, pageSize: 20 })
    ).rejects.toBeInstanceOf(AppError);
  });

  it("paginates the contact's email history ordered by most recent", async () => {
    for (let i = 1; i <= 3; i++) {
      const log = await campaignLogsRepository.upsertPending(`campaign-${i}`, "contact-1");
      await campaignLogsRepository.markSuccess(log.id);
      const stored = campaignLogsRepository.items.find((item) => item.id === log.id)!;
      stored.campaign = makeCampaign(`campaign-${i}`, `Assunto ${i}`);
    }

    const page1 = await sut.execute({ contactId: "contact-1", page: 1, pageSize: 2 });
    expect(page1.items).toHaveLength(2);
    expect(page1.total).toBe(3);
    expect(page1.page).toBe(1);
    expect(page1.pageSize).toBe(2);

    const page2 = await sut.execute({ contactId: "contact-1", page: 2, pageSize: 2 });
    expect(page2.items).toHaveLength(1);
  });

  it("marks an item as opened, with the correct open count, when an OPEN event was recorded", async () => {
    const log = await campaignLogsRepository.upsertPending("campaign-1", "contact-1");
    await campaignLogsRepository.markSuccess(log.id);
    const stored = campaignLogsRepository.items.find((item) => item.id === log.id)!;
    stored.campaign = makeCampaign("campaign-1", "Assunto Aberto");

    await emailEventsRepository.create({
      type: "OPEN",
      messageId: "pixel:log-1:1",
      campaignId: "campaign-1",
      contactId: "contact-1",
      payload: {},
    });
    await emailEventsRepository.create({
      type: "OPEN",
      messageId: "pixel:log-1:2",
      campaignId: "campaign-1",
      contactId: "contact-1",
      payload: {},
    });

    const result = await sut.execute({ contactId: "contact-1", page: 1, pageSize: 20 });

    expect(result.items[0].opened).toBe(true);
    expect(result.items[0].openCount).toBe(2);
  });

  it("filters to only unopened items when status=unopened", async () => {
    const openedLog = await campaignLogsRepository.upsertPending("campaign-1", "contact-1");
    await campaignLogsRepository.markSuccess(openedLog.id);
    campaignLogsRepository.items.find((item) => item.id === openedLog.id)!.campaign = makeCampaign(
      "campaign-1",
      "Aberto"
    );
    campaignLogsRepository.openedLogIds.add(openedLog.id);

    const unopenedLog = await campaignLogsRepository.upsertPending("campaign-2", "contact-1");
    await campaignLogsRepository.markSuccess(unopenedLog.id);
    campaignLogsRepository.items.find((item) => item.id === unopenedLog.id)!.campaign = makeCampaign(
      "campaign-2",
      "Não Aberto"
    );

    const result = await sut.execute({ contactId: "contact-1", page: 1, pageSize: 20, status: "unopened" });

    expect(result.total).toBe(1);
    expect(result.items[0].subject).toBe("Não Aberto");
  });
});
