import { EmailEvent } from "@prisma/client";
import {
  AnalyticsEmailEvent,
  CampaignEngagementSummary,
  CampaignOpenEvent,
  CampaignOpenerSummary,
  CampaignOpenStats,
  CreateEmailEventInput,
  EmailEventsRepository,
} from "./email-events-repository";

export class InMemoryEmailEventsRepository implements EmailEventsRepository {
  public items: EmailEvent[] = [];

  async create(data: CreateEmailEventInput): Promise<EmailEvent> {
    const event: EmailEvent = {
      id: `event-${this.items.length + 1}`,
      type: data.type,
      messageId: data.messageId,
      campaignId: data.campaignId ?? null,
      contactId: data.contactId ?? null,
      payload: data.payload as never,
      createdAt: new Date(),
    };
    this.items.push(event);
    return event;
  }

  async findEventsForAnalytics(
    campaignIds: string[],
    startDate?: Date,
    endDate?: Date
  ): Promise<AnalyticsEmailEvent[]> {
    return this.items
      .filter((item) => {
        if (!item.campaignId || !campaignIds.includes(item.campaignId)) return false;
        if (startDate && item.createdAt < startDate) return false;
        if (endDate && item.createdAt > endDate) return false;
        return true;
      })
      .map((item) => ({ type: item.type, createdAt: item.createdAt }));
  }

  async findEngagementByContactAndCampaignIds(
    contactId: string,
    campaignIds: string[]
  ): Promise<CampaignEngagementSummary[]> {
    const relevant = this.items.filter(
      (item) => item.contactId === contactId && item.campaignId && campaignIds.includes(item.campaignId)
    );

    const summaryByCampaignId = new Map<string, CampaignEngagementSummary>();
    for (const item of relevant) {
      if (!item.campaignId || (item.type !== "OPEN" && item.type !== "CLICK")) continue;
      const summary = summaryByCampaignId.get(item.campaignId) ?? {
        campaignId: item.campaignId,
        openCount: 0,
        clickCount: 0,
        firstOpenedAt: null,
      };

      if (item.type === "OPEN") {
        summary.openCount += 1;
        if (!summary.firstOpenedAt || item.createdAt < summary.firstOpenedAt) {
          summary.firstOpenedAt = item.createdAt;
        }
      } else {
        summary.clickCount += 1;
      }

      summaryByCampaignId.set(item.campaignId, summary);
    }

    return [...summaryByCampaignId.values()];
  }

  async getCampaignOpenStats(campaignId: string): Promise<CampaignOpenStats> {
    const opens = this.items.filter((item) => item.campaignId === campaignId && item.type === "OPEN");
    const uniqueContactIds = new Set(opens.map((item) => item.contactId).filter(Boolean));

    return { totalOpens: opens.length, uniqueOpens: uniqueContactIds.size };
  }

  async findOpenersByCampaignId(campaignId: string): Promise<CampaignOpenerSummary[]> {
    const opens = this.items.filter(
      (item) => item.campaignId === campaignId && item.type === "OPEN" && item.contactId
    );

    const byContactId = new Map<string, CampaignOpenerSummary>();
    for (const item of opens) {
      const contactId = item.contactId as string;
      const existing = byContactId.get(contactId);
      if (existing) {
        existing.openCount += 1;
        if (item.createdAt < existing.firstOpenedAt) existing.firstOpenedAt = item.createdAt;
      } else {
        byContactId.set(contactId, { contactId, firstOpenedAt: item.createdAt, openCount: 1 });
      }
    }

    return [...byContactId.values()];
  }

  async findOpenEventsByCampaignId(campaignId: string): Promise<CampaignOpenEvent[]> {
    return this.items
      .filter((item) => item.campaignId === campaignId && item.type === "OPEN")
      .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
      .map((item) => ({ createdAt: item.createdAt }));
  }
}
