import { CampaignLog, Contact } from "@prisma/client";
import { prisma } from "@/shared/infra/prisma/prisma-client";
import { CampaignWithSender } from "./campaigns-repository";
import {
  CampaignLogsRepository,
  CampaignLogStatusCounts,
  CampaignLogWithCampaignAndSenderName,
  FindByContactIdOptions,
} from "./campaign-logs-repository";

export class PrismaCampaignLogsRepository implements CampaignLogsRepository {
  async upsertPending(campaignId: string, contactId: string): Promise<CampaignLog> {
    return prisma.campaignLog.upsert({
      where: { campaignId_contactId: { campaignId, contactId } },
      create: { campaignId, contactId, status: "PENDING" },
      update: { status: "PENDING" },
    });
  }

  async markSuccess(logId: string): Promise<void> {
    await prisma.campaignLog.update({
      where: { id: logId },
      data: { status: "SUCCESS", sentAt: new Date(), errorMessage: null },
    });
  }

  async markFailed(logId: string, errorMessage: string): Promise<void> {
    await prisma.campaignLog.update({
      where: { id: logId },
      data: { status: "FAILED", errorMessage },
    });
  }

  async incrementRetryCount(logId: string): Promise<void> {
    await prisma.campaignLog.update({
      where: { id: logId },
      data: { retryCount: { increment: 1 } },
    });
  }

  async updateMessageId(logId: string, messageId: string): Promise<void> {
    await prisma.campaignLog.update({
      where: { id: logId },
      data: { messageId },
    });
  }

  async findByMessageId(messageId: string): Promise<(CampaignLog & { contact: Contact }) | null> {
    return prisma.campaignLog.findFirst({
      where: { messageId },
      include: { contact: true },
    });
  }

  async findFailedByCampaignId(campaignId: string): Promise<(CampaignLog & { contact: Contact })[]> {
    return prisma.campaignLog.findMany({
      where: { campaignId, status: "FAILED" },
      include: { contact: true },
    });
  }

  async countFailedByCampaignId(campaignId: string): Promise<number> {
    return prisma.campaignLog.count({
      where: { campaignId, status: "FAILED" },
    });
  }

  async countByStatusForCampaignIds(campaignIds: string[]): Promise<CampaignLogStatusCounts> {
    const counts: CampaignLogStatusCounts = { total: 0, success: 0, failed: 0, pending: 0 };
    if (campaignIds.length === 0) {
      return counts;
    }

    const grouped = await prisma.campaignLog.groupBy({
      by: ["status"],
      where: { campaignId: { in: campaignIds } },
      _count: { _all: true },
    });

    for (const group of grouped) {
      counts.total += group._count._all;
      if (group.status === "SUCCESS") counts.success = group._count._all;
      if (group.status === "FAILED") counts.failed = group._count._all;
      if (group.status === "PENDING") counts.pending = group._count._all;
    }

    return counts;
  }

  async findCampaignAndContactIdsByLogId(
    logId: string
  ): Promise<{ campaignId: string; contactId: string } | null> {
    return prisma.campaignLog.findUnique({
      where: { id: logId },
      select: { campaignId: true, contactId: true },
    });
  }

  async findByIdWithCampaignAndContact(
    logId: string
  ): Promise<(CampaignLog & { campaign: CampaignWithSender; contact: Contact }) | null> {
    return prisma.campaignLog.findUnique({
      where: { id: logId },
      include: {
        campaign: { include: { sender: { include: { domain: true } }, contactList: { select: { id: true, name: true } } } },
        contact: true,
      },
    });
  }

  private buildContactStatusWhere(contactId: string, status?: "opened" | "unopened") {
    if (status === "opened") {
      return { contactId, campaign: { emailEvents: { some: { contactId, type: "OPEN" as const } } } };
    }
    if (status === "unopened") {
      return { contactId, campaign: { emailEvents: { none: { contactId, type: "OPEN" as const } } } };
    }
    return { contactId };
  }

  async findByContactId(
    contactId: string,
    { skip, take, status }: FindByContactIdOptions
  ): Promise<CampaignLogWithCampaignAndSenderName[]> {
    return prisma.campaignLog.findMany({
      where: this.buildContactStatusWhere(contactId, status),
      include: { campaign: { include: { sender: { select: { name: true, fromEmail: true } } } } },
      orderBy: { createdAt: "desc" },
      skip,
      take,
    });
  }

  async countByContactId(contactId: string, { status }: { status?: "opened" | "unopened" }): Promise<number> {
    return prisma.campaignLog.count({
      where: this.buildContactStatusWhere(contactId, status),
    });
  }

  async findLogsForAnalytics(
    campaignIds: string[],
    startDate?: Date,
    endDate?: Date
  ): Promise<
    Array<{
      id: string;
      status: string;
      createdAt: Date;
      sentAt: Date | null;
      retryCount: number;
      contact: { email: string };
    }>
  > {
    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.campaignLog.findMany({
      where: whereClause,
      select: {
        id: true,
        status: true,
        createdAt: true,
        sentAt: true,
        retryCount: true,
        contact: {
          select: {
            email: true,
          },
        },
      },
      orderBy: {
        createdAt: "asc",
      },
    });
  }
}

