import { AppError } from "@/core/errors/app-error";
import { CampaignLogsRepository } from "@/modules/campaigns/repositories/campaign-logs-repository";
import { EmailEventsRepository } from "@/modules/ses-webhooks/repositories/email-events-repository";
import { ContactsRepository } from "../repositories/contacts-repository";
import {
  ContactEmailHistoryItem,
  mapCampaignLogToEmailHistoryItem,
} from "../entities/map-campaign-log-to-email-history-item";

const MAX_PAGE_SIZE = 100;

interface GetContactEmailHistoryUseCaseRequest {
  contactId: string;
  page: number;
  pageSize: number;
  status?: "opened" | "unopened";
}

interface GetContactEmailHistoryUseCaseResponse {
  items: ContactEmailHistoryItem[];
  total: number;
  page: number;
  pageSize: number;
}

export class GetContactEmailHistoryUseCase {
  constructor(
    private contactsRepository: ContactsRepository,
    private campaignLogsRepository: CampaignLogsRepository,
    private emailEventsRepository: EmailEventsRepository
  ) {}

  async execute({
    contactId,
    page,
    pageSize,
    status,
  }: GetContactEmailHistoryUseCaseRequest): Promise<GetContactEmailHistoryUseCaseResponse> {
    const contact = await this.contactsRepository.findById(contactId);
    if (!contact) {
      throw AppError.notFound("Contato não encontrado");
    }

    const clampedPage = Math.max(page, 1);
    const clampedPageSize = Math.min(Math.max(pageSize, 1), MAX_PAGE_SIZE);
    const skip = (clampedPage - 1) * clampedPageSize;

    const [logs, total] = await Promise.all([
      this.campaignLogsRepository.findByContactId(contactId, { skip, take: clampedPageSize, status }),
      this.campaignLogsRepository.countByContactId(contactId, { status }),
    ]);

    const campaignIds = [...new Set(logs.map((log) => log.campaignId))];
    const engagement = await this.emailEventsRepository.findEngagementByContactAndCampaignIds(
      contactId,
      campaignIds
    );
    const engagementByCampaignId = new Map(engagement.map((entry) => [entry.campaignId, entry]));

    const items = logs.map((log) =>
      mapCampaignLogToEmailHistoryItem(log, contact.email, engagementByCampaignId.get(log.campaignId))
    );

    return { items, total, page: clampedPage, pageSize: clampedPageSize };
  }
}
