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

export class PrismaEmailEventsRepository implements EmailEventsRepository {
  async create(data: CreateEmailEventInput) {
    return prisma.emailEvent.create({
      data: {
        type: data.type,
        messageId: data.messageId,
        campaignId: data.campaignId ?? undefined,
        contactId: data.contactId ?? undefined,
        payload: data.payload as never,
      },
    });
  }

  async findEventsForAnalytics(
    campaignIds: string[],
    startDate?: Date,
    endDate?: Date
  ): Promise<AnalyticsEmailEvent[]> {
    if (campaignIds.length === 0) {
      return [];
    }

    const whereClause: {
      campaignId: { in: string[] };
      createdAt?: { gte?: Date; lte?: Date };
    } = {
      campaignId: { in: campaignIds },
    };

    if (startDate || endDate) {
      whereClause.createdAt = {};
      if (startDate) whereClause.createdAt.gte = startDate;
      if (endDate) whereClause.createdAt.lte = endDate;
    }

    return prisma.emailEvent.findMany({
      where: whereClause,
      select: { type: true, createdAt: true },
      orderBy: { createdAt: "asc" },
    });
  }

  async findEngagementByContactAndCampaignIds(
    contactId: string,
    campaignIds: string[]
  ): Promise<CampaignEngagementSummary[]> {
    if (campaignIds.length === 0) {
      return [];
    }

    const grouped = await prisma.emailEvent.groupBy({
      by: ["campaignId", "type"],
      where: { contactId, campaignId: { in: campaignIds }, type: { in: ["OPEN", "CLICK"] } },
      _count: { _all: true },
      _min: { createdAt: true },
    });

    const summaryByCampaignId = new Map<string, CampaignEngagementSummary>();
    for (const group of grouped) {
      if (!group.campaignId) continue;
      const summary = summaryByCampaignId.get(group.campaignId) ?? {
        campaignId: group.campaignId,
        openCount: 0,
        clickCount: 0,
        firstOpenedAt: null,
      };

      if (group.type === "OPEN") {
        summary.openCount = group._count._all;
        summary.firstOpenedAt = group._min.createdAt;
      } else if (group.type === "CLICK") {
        summary.clickCount = group._count._all;
      }

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

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

  async getCampaignOpenStats(campaignId: string): Promise<CampaignOpenStats> {
    const [totalOpens, uniqueOpenGroups] = await Promise.all([
      prisma.emailEvent.count({ where: { campaignId, type: "OPEN" } }),
      prisma.emailEvent.groupBy({ by: ["contactId"], where: { campaignId, type: "OPEN" } }),
    ]);

    return { totalOpens, uniqueOpens: uniqueOpenGroups.length };
  }

  async findOpenersByCampaignId(campaignId: string): Promise<CampaignOpenerSummary[]> {
    const grouped = await prisma.emailEvent.groupBy({
      by: ["contactId"],
      where: { campaignId, type: "OPEN", contactId: { not: null } },
      _count: { _all: true },
      _min: { createdAt: true },
    });

    return grouped
      .filter((group) => group.contactId !== null && group._min.createdAt !== null)
      .map((group) => ({
        contactId: group.contactId as string,
        firstOpenedAt: group._min.createdAt as Date,
        openCount: group._count._all,
      }));
  }

  async findOpenEventsByCampaignId(campaignId: string): Promise<CampaignOpenEvent[]> {
    return prisma.emailEvent.findMany({
      where: { campaignId, type: "OPEN" },
      select: { createdAt: true },
      orderBy: { createdAt: "asc" },
    });
  }
}
