import { InjectQueue } from "@nestjs/bullmq";
import { Injectable, Logger } from "@nestjs/common";
import { Prisma, WebhookEventOutStatus } from "@prisma/client";
import { Queue } from "bullmq";
import { newId } from "../common/ids/id.util";
import { PrismaService } from "../prisma/prisma.service";
import { WEBHOOK_DELIVERY_QUEUE, WebhookEventType } from "./webhook-events.constants";

interface EmitOptions {
  merchantId: string;
  eventType: WebhookEventType;
  data: Record<string, unknown>;
  paymentIntentId?: string;
}

@Injectable()
export class WebhookEventService {
  private readonly logger = new Logger(WebhookEventService.name);

  constructor(
    private readonly prisma: PrismaService,
    @InjectQueue(WEBHOOK_DELIVERY_QUEUE) private readonly queue: Queue,
  ) {}

  /** Emite un evento. Busca endpoints suscriptos, persiste un WebhookEventOut por cada uno y encola delivery. */
  async emit({ merchantId, eventType, data, paymentIntentId }: EmitOptions): Promise<void> {
    const endpoints = await this.prisma.webhookEndpoint.findMany({
      where: { merchantId, active: true },
      select: { id: true, subscribedEvents: true },
    });

    const subscribed = endpoints.filter((ep) =>
      ep.subscribedEvents.includes("*") || ep.subscribedEvents.includes(eventType),
    );

    if (subscribed.length === 0) {
      this.logger.debug(`No endpoints subscribed for ${eventType} on merchant ${merchantId}`);
      return;
    }

    const eventId = newId("webhookEventOut");
    const createdAt = new Date();

    const payload = {
      id: eventId,
      event_type: eventType,
      created_at: createdAt.toISOString(),
      data,
    };

    await Promise.all(
      subscribed.map(async (ep) => {
        const row = await this.prisma.webhookEventOut.create({
          data: {
            id: newId("webhookEventOut"),
            endpointId: ep.id,
            intentId: paymentIntentId ?? null,
            eventType,
            payloadJson: payload as unknown as Prisma.InputJsonValue,
            status: WebhookEventOutStatus.pending,
            nextAttemptAt: createdAt,
          },
        });
        await this.queue.add(
          "deliver",
          { eventOutId: row.id },
          {
            jobId: row.id,
            removeOnComplete: true,
            removeOnFail: 1000,
          },
        );
      }),
    );
  }
}
