import { Router } from 'express';
import Joi from 'joi';
import type { WhereOptions } from 'sequelize';
import { Op } from 'sequelize';

import { createListParamSchema, getOrder } from '../libs/api';
import { authenticate } from '../libs/security';
import { Event } from '../store/models/event';
import { blocklet } from '../libs/auth';
import logger from '../libs/logger';
import { addWebhookJob } from '../queues/webhook';
import { WebhookEndpoint } from '../store/models/webhook-endpoint';
import { Subscription } from '../store/models/subscription';

const router = Router();
const auth = authenticate<Event>({ component: true, roles: ['owner', 'admin'] });

const schema = createListParamSchema<{
  type?: string;
  object_id?: string;
}>({
  type: Joi.string().empty(''),
  object_id: Joi.string().empty(''),
});
router.get('/', auth, async (req, res) => {
  const { page, pageSize, ...query } = await schema.validateAsync(req.query, { stripUnknown: true });
  const where: WhereOptions<Event> = {};

  if (query.type) {
    where.type = query.type
      .split(',')
      .map((x) => x.trim())
      .filter(Boolean);
  }
  if (query.object_id) {
    where.object_id = query.object_id
      .split(',')
      .map((x) => x.trim())
      .filter(Boolean);
  }
  if (typeof query.livemode === 'boolean') {
    where.livemode = query.livemode;
  }

  try {
    const { rows: list, count } = await Event.findAndCountAll({
      where,
      attributes: { exclude: ['data', 'request'] },
      order: getOrder(req.query, [['created_at', 'DESC']]),
      offset: (page - 1) * pageSize,
      limit: pageSize,
      include: [],
    });

    res.json({ count, list, paging: { page, pageSize } });
  } catch (err) {
    logger.error(err);
    res.json({ count: 0, list: [], paging: { page, pageSize } });
  }
});

const retryWebhooksSchema = createListParamSchema<{
  eventType?: string;
  objectType?: string;
  objectId?: string;
  objectIds?: string;
  eventIds?: string;
  subscriptionStatus?: string;
  latestOnly?: boolean;
}>({
  eventType: Joi.string().empty(''),
  objectType: Joi.string().empty(''),
  objectId: Joi.string().empty(''),
  objectIds: Joi.string().empty(''),
  eventIds: Joi.string().empty(''),
  subscriptionStatus: Joi.string().empty(''),
  latestOnly: Joi.boolean().optional(),
});

router.get('/retry-webhooks', auth, async (req, res) => {
  try {
    const { eventType, objectType, objectId, objectIds, eventIds, subscriptionStatus, latestOnly } =
      await retryWebhooksSchema.validateAsync(req.query, {
        stripUnknown: true,
      });

    const where: WhereOptions<Event> = { livemode: req.livemode };
    let targetObjectIds: string[] = [];

    // Handle subscription status filter
    if (subscriptionStatus) {
      const subscriptions = await Subscription.findAll({
        where: {
          status: subscriptionStatus,
          livemode: req.livemode,
        },
        attributes: ['id'],
      });

      if (subscriptions.length === 0) {
        return res.json({
          message: `No subscriptions found with status: ${subscriptionStatus}`,
          scheduled: 0,
          eventsProcessed: 0,
        });
      }

      targetObjectIds = subscriptions.map((sub) => sub.id);
      where.object_type = 'subscription';
      where.object_id = { [Op.in]: targetObjectIds };

      logger.info(`Found ${targetObjectIds.length} subscriptions with status: ${subscriptionStatus}`);
    }

    // Handle explicit object filters
    if (objectType) {
      where.object_type = objectType;
    }

    if (objectId) {
      where.object_id = objectId;
    }

    if (objectIds) {
      const ids = objectIds
        .split(',')
        .map((x) => x.trim())
        .filter(Boolean);
      where.object_id = ids.length > 1 ? { [Op.in]: ids } : ids[0];
    }

    if (eventType) {
      where.type = eventType;
    }

    if (eventIds) {
      const ids = eventIds
        .split(',')
        .map((x) => x.trim())
        .filter(Boolean);
      where.id = ids.length > 1 ? { [Op.in]: ids } : ids[0];
    }

    let events = await Event.findAll({
      where,
      order: [['created_at', 'DESC']],
    });

    if (events.length === 0) {
      return res.json({
        message: 'No events found matching the criteria',
        scheduled: 0,
        eventsProcessed: 0,
      });
    }

    // If latestOnly is true, group by object_id and keep only the latest event for each
    if (latestOnly) {
      const eventsByObject = new Map<string, Event>();
      events.forEach((event) => {
        if (!eventsByObject.has(event.object_id)) {
          eventsByObject.set(event.object_id, event);
        }
      });
      events = Array.from(eventsByObject.values());
      logger.info(`Filtered to ${events.length} latest events (from ${eventsByObject.size} unique objects)`);
    }

    const webhooks = await WebhookEndpoint.findAll({
      where: { status: 'enabled', livemode: req.livemode },
    });

    if (webhooks.length === 0) {
      return res.json({
        message: 'No enabled webhook endpoints found',
        scheduled: 0,
        eventsProcessed: events.length,
      });
    }

    let scheduled = 0;
    // eslint-disable-next-line no-restricted-syntax
    for (const event of events) {
      const eventWebhooks = webhooks.filter((webhook) => webhook.enabled_events.includes(event.type));

      // eslint-disable-next-line no-restricted-syntax
      for (const webhook of eventWebhooks) {
        // eslint-disable-next-line no-await-in-loop
        const added = await addWebhookJob(event.id, webhook.id, { persist: true });
        if (added) {
          scheduled += 1;
        }
      }
    }

    logger.info('Batch webhook retry completed', {
      eventsProcessed: events.length,
      webhooksScheduled: scheduled,
      criteria: { eventType, objectType, objectId, subscriptionStatus, latestOnly },
    });

    return res.json({
      message: `Successfully scheduled ${scheduled} webhooks for retry across ${events.length} events`,
      scheduled,
      eventsProcessed: events.length,
    });
  } catch (err: any) {
    logger.error('Failed to batch retry webhooks', err);
    return res.status(500).json({ error: `Failed to retry webhooks: ${err.message}` });
  }
});

router.get('/:id', auth, async (req, res) => {
  try {
    const doc = await Event.findOne({
      where: { id: req.params.id },
      include: [],
    });

    if (doc) {
      const requestedBy = doc.request?.requested_by || 'system';
      const { user } = await blocklet.getUser(requestedBy as string);
      if (user) {
        return res.json({ ...doc.toJSON(), requestInfo: user });
      }
      return res.json(doc);
    }
    return res.status(404).json(null);
  } catch (err) {
    logger.error(err);
    return res.status(400).json({ error: `Failed to get event: ${err.message}` });
  }
});

router.post('/:id/retry-webhooks', auth, async (req, res) => {
  try {
    const event = await Event.findOne({
      where: { id: req.params.id },
    });

    if (!event) {
      return res.status(404).json({ error: 'Event not found' });
    }

    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) {
      return res.json({ message: 'No enabled webhook endpoints found for this event type', scheduled: 0 });
    }

    let scheduled = 0;
    // eslint-disable-next-line no-restricted-syntax
    for (const webhook of eventWebhooks) {
      // eslint-disable-next-line no-await-in-loop
      const added = await addWebhookJob(event.id, webhook.id, { persist: true });
      if (added) {
        scheduled += 1;
        logger.info('Manually scheduled webhook retry', { eventId: event.id, webhookId: webhook.id });
      }
    }

    return res.json({
      message: `Successfully scheduled ${scheduled} webhooks for retry`,
      scheduled,
      total: eventWebhooks.length,
    });
  } catch (err: any) {
    logger.error('Failed to retry webhooks for event', err);
    return res.status(500).json({ error: `Failed to retry webhooks: ${err.message}` });
  }
});

export default router;
