import { Injectable, Logger } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { InjectQueue } from '@nestjs/bullmq';
import { Queue } from 'bullmq';
import { PrismaService } from '../../prisma/prisma.service';
import { WebhookJobData } from './webhook-delivery.processor';

/** Backoff delays for 3 retry attempts: 5s, 30s, 5min */
const RETRY_DELAYS = [5000, 30000, 300000];

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

  constructor(
    @InjectQueue('webhook-delivery')
    private readonly webhookQueue: Queue,
    private readonly prisma: PrismaService,
  ) {}

  @OnEvent('client.created')
  async onClientCreated(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'client.created', payload.data);
  }

  @OnEvent('client.updated')
  async onClientUpdated(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'client.updated', payload.data);
  }

  @OnEvent('training.created')
  async onTrainingCreated(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'training.created', payload.data);
  }

  @OnEvent('training.cancelled')
  async onTrainingCancelled(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'training.cancelled', payload.data);
  }

  @OnEvent('training.rescheduled')
  async onTrainingRescheduled(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'training.rescheduled', payload.data);
  }

  @OnEvent('training.completed')
  async onTrainingCompleted(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'training.completed', payload.data);
  }

  @OnEvent('sale.created')
  async onSaleCreated(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'sale.created', payload.data);
  }

  @OnEvent('sale.paid')
  async onSalePaid(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'sale.paid', payload.data);
  }

  @OnEvent('funnel.stage_changed')
  async onFunnelStageChanged(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'funnel.stage_changed', payload.data);
  }

  @OnEvent('funnel.assigned')
  async onFunnelAssigned(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'funnel.assigned', payload.data);
  }

  @OnEvent('schedule.updated')
  async onScheduleUpdated(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'schedule.updated', payload.data);
  }

  @OnEvent('membership.expiring')
  async onMembershipExpiring(payload: { clubId: string; data: any }) {
    await this.dispatch(payload.clubId, 'membership.expiring', payload.data);
  }

  private async dispatch(
    clubId: string,
    event: string,
    data: any,
  ): Promise<void> {
    const subscriptions = await this.prisma.webhookSubscription.findMany({
      where: {
        clubId,
        isActive: true,
        events: { has: event },
      },
      select: { id: true, url: true, secret: true },
    });

    if (subscriptions.length === 0) return;

    const webhookPayload = {
      event,
      timestamp: new Date().toISOString(),
      club_id: clubId,
      data,
    };

    for (const sub of subscriptions) {
      const jobData: WebhookJobData = {
        clubId,
        subscriptionId: sub.id,
        event,
        payload: webhookPayload,
        secret: sub.secret,
        url: sub.url,
      };

      await this.webhookQueue.add(`webhook:${event}`, jobData, {
        attempts: 3,
        backoff: {
          type: 'custom',
        },
        removeOnComplete: 100,
        removeOnFail: 500,
      });

      this.logger.debug(
        `Enqueued webhook: event=${event}, subscription=${sub.id}, url=${sub.url}`,
      );
    }
  }
}

export function calculateBackoff(attemptsMade: number): number {
  return RETRY_DELAYS[Math.min(attemptsMade - 1, RETRY_DELAYS.length - 1)];
}
