import { Op } from 'sequelize';

import { events } from '../libs/event';
import logger from '../libs/logger';
import createQueue from '../libs/queue';
import { Event } from '../store/models/event';
import { WebhookAttempt } from '../store/models/webhook-attempt';
import { WebhookEndpoint } from '../store/models/webhook-endpoint';
import { addWebhookJob } from './webhook';

type EventJob = {
  eventId: string;
};

export const handleEvent = async (job: EventJob) => {
  logger.info('Starting to handle event', job);

  const event = await Event.findByPk(job.eventId);
  if (!event) {
    logger.warn('Event not found', job);
    return;
  }

  if (!event.pending_webhooks) {
    logger.warn('Event already processed', job);
    return;
  }

  const webhooks = await WebhookEndpoint.findAll({ where: { status: 'enabled', livemode: event.livemode } });
  const eventWebhooks = webhooks.filter((webhook) => webhook.enabled_events.includes(event.type));
  if (eventWebhooks.length === 0) {
    logger.info('no webhook endpoint for event', job);
    await event.update({ pending_webhooks: 0 });
    return;
  }

  await event.update({ pending_webhooks: eventWebhooks.length });
  logger.info(`Updated event ${event.id} with ${eventWebhooks.length} pending webhooks`);

  eventWebhooks.forEach(async (webhook) => {
    const attemptCount = await WebhookAttempt.count({
      where: {
        event_id: event.id,
        webhook_endpoint_id: webhook.id,
        response_status: {
          [Op.gte]: 200,
          [Op.lt]: 300,
        },
      },
    });

    // we should only push webhook if it's not successfully attempted before
    if (attemptCount === 0) {
      logger.info(`Scheduling attempt for event ${event.id} and webhook ${webhook.id}`, job);
      await addWebhookJob(event.id, webhook.id, { persist: false });
    }
  });

  logger.info(`Finished handling event ${job.eventId}`);
};

export const eventQueue = createQueue<EventJob>({
  name: 'event',
  onJob: handleEvent,
  options: {
    concurrency: 5,
    maxRetries: 3,
  },
});

export const startEventQueue = async () => {
  const docs = await Event.findAll({
    where: {
      pending_webhooks: { [Op.gt]: 0 },
    },
    attributes: ['id'],
  });

  logger.info(`Found ${docs.length} events with pending webhooks`);

  docs.forEach(async (x) => {
    const exist = await eventQueue.get(x.id);
    if (!exist) {
      logger.info(`Pushing event ${x.id} to queue`);
      eventQueue.push({ id: x.id, job: { eventId: x.id }, persist: false });
    }
  });

  logger.info('Finished starting event queue');
};

eventQueue.on('failed', ({ id, job, error }) => {
  logger.error('event job failed', { id, job, error });
});

events.on('event.created', (event) => {
  eventQueue.push({ id: event.id, job: { eventId: event.id }, persist: false });
});
