import { prisma } from "../constants";
import GenericError from "../Errors/GenericError";
import { GeneratedQuoteController } from "../controllers/GeneratedQuoteController";

const generatedQuoteInclude = {
  opportunity: true,
  createdBy: {
    include: {
      roles: true,
    },
  },
} as const;

export const generatedQuotes = {
  async fetch(id: string): Promise<GeneratedQuoteController> {
    const quote = await prisma.generatedQuotes.findFirst({
      where: { id },
      include: generatedQuoteInclude,
    });

    if (!quote) {
      throw new GenericError({
        message: "Generated quote not found",
        name: "GeneratedQuoteNotFound",
        cause: `No generated quote exists with ID '${id}'`,
        status: 404,
      });
    }

    return new GeneratedQuoteController(quote);
  },

  async fetchByOpportunity(
    opportunityId: string
  ): Promise<GeneratedQuoteController[]> {
    const rows = await prisma.generatedQuotes.findMany({
      where: { opportunityId },
      include: generatedQuoteInclude,
      orderBy: { createdAt: "desc" },
    });

    return rows.map((row) => new GeneratedQuoteController(row));
  },

  async fetchByCreator(
    createdById: string
  ): Promise<GeneratedQuoteController[]> {
    const rows = await prisma.generatedQuotes.findMany({
      where: { createdById },
      include: generatedQuoteInclude,
      orderBy: { createdAt: "desc" },
    });

    return rows.map((row) => new GeneratedQuoteController(row));
  },

  async fetchByHash(
    quoteRegenHash: string
  ): Promise<GeneratedQuoteController | null> {
    const quote = await prisma.generatedQuotes.findUnique({
      where: { quoteRegenHash },
      include: generatedQuoteInclude,
    });

    return quote ? new GeneratedQuoteController(quote) : null;
  },

  async create(data: {
    id?: string;
    quoteRegenData: unknown;
    quoteRegenParams: unknown;
    quoteRegenHash: string;
    quoteFile: Buffer | Uint8Array;
    quoteFileName: string;
    opportunityId: string;
    createdById: string;
  }): Promise<GeneratedQuoteController> {
    const opportunity = await prisma.opportunity.findFirst({
      where: { uid: data.opportunityId },
      select: { uid: true },
    });

    if (!opportunity) {
      throw new GenericError({
        message: "Opportunity not found",
        name: "OpportunityNotFound",
        cause: `No opportunity exists with uid '${data.opportunityId}'`,
        status: 404,
      });
    }

    const createdBy = await prisma.user.findFirst({
      where: { id: data.createdById },
      select: { id: true },
    });

    if (!createdBy) {
      throw new GenericError({
        message: "User not found",
        name: "UserNotFound",
        cause: `No user exists with ID '${data.createdById}'`,
        status: 404,
      });
    }

    const quote = await prisma.generatedQuotes.create({
      data: {
        ...(data.id ? { id: data.id } : {}),
        quoteRegenData: data.quoteRegenData as any,
        quoteRegenParams: data.quoteRegenParams as any,
        quoteRegenHash: data.quoteRegenHash,
        quoteFile: Buffer.from(data.quoteFile),
        quoteFileName: data.quoteFileName,
        opportunityId: data.opportunityId,
        createdById: data.createdById,
      },
      include: generatedQuoteInclude,
    });

    return new GeneratedQuoteController(quote);
  },

  async delete(id: string): Promise<void> {
    const existing = await prisma.generatedQuotes.findFirst({
      where: { id },
      select: { id: true },
    });

    if (!existing) {
      throw new GenericError({
        message: "Generated quote not found",
        name: "GeneratedQuoteNotFound",
        cause: `No generated quote exists with ID '${id}'`,
        status: 404,
      });
    }

    await prisma.generatedQuotes.delete({
      where: { id },
    });
  },

  async recordDownload(
    id: string,
    user: {
      id: string;
      name?: string | null;
      email: string;
      fetchAction: string;
    }
  ): Promise<GeneratedQuoteController> {
    const existing = await prisma.generatedQuotes.findFirst({
      where: { id },
      select: { id: true, downloads: true },
    });

    if (!existing) {
      throw new GenericError({
        message: "Generated quote not found",
        name: "GeneratedQuoteNotFound",
        cause: `No generated quote exists with ID '${id}'`,
        status: 404,
      });
    }

    const currentDownloads = Array.isArray(existing.downloads)
      ? (existing.downloads as unknown[])
      : [];

    const downloadRecord = {
      downloadedAt: new Date().toISOString(),
      fetchAction: user.fetchAction,
      userId: user.id,
      userName: user.name ?? null,
      userEmail: user.email,
    };

    const updated = await prisma.generatedQuotes.update({
      where: { id },
      data: {
        downloads: [...currentDownloads, downloadRecord] as any,
      },
      include: generatedQuoteInclude,
    });

    return new GeneratedQuoteController(updated);
  },
};
