import pAll from 'p-all';
import createQueue from '../libs/queue';
import { Coupon, PromotionCode } from '../store/models';
import logger from '../libs/logger';
import { events } from '../libs/event';
import { validCoupon, validPromotionCode } from '../libs/discount/coupon';

export type DiscountType = 'coupon' | 'promotion-code';

export interface DiscountStatusJobData {
  type: DiscountType;
  id: string;
}

/**
 * Process discount status check for both coupons and promotion codes
 */
export async function processDiscountStatus(job: DiscountStatusJobData) {
  logger.info('Processing discount status check job', { job });
  const { type, id } = job;

  if (type === 'coupon') {
    await processCouponStatus(id);
  } else if (type === 'promotion-code') {
    await processPromotionCodeStatus(id);
  }
}

/**
 * Process coupon status check
 */
async function processCouponStatus(couponId: string) {
  const coupon = await Coupon.findByPk(couponId);
  if (!coupon) {
    return;
  }

  // Skip if already invalid
  if (!coupon.valid) {
    logger.info('Coupon is already invalid, skipping', { couponId });
    return;
  }

  // Use the existing validCoupon function to check validity
  const validation = validCoupon(coupon);

  if (!validation.valid) {
    // Mark coupon as invalid
    await coupon.update({ valid: false, metadata: { ...coupon.metadata, invalid_reason: validation.reason } });
    logger.info('Coupon marked as invalid', {
      couponId,
      reason: validation.reason,
      redeem_by: coupon.redeem_by,
      max_redemptions: coupon.max_redemptions,
      times_redeemed: coupon.times_redeemed,
    });
    const promotionCodes = await PromotionCode.findAll({ where: { coupon_id: couponId, active: true } });
    await pAll(
      promotionCodes.map(
        (promotionCode) => () =>
          promotionCode.update({
            active: false,
            metadata: { ...promotionCode.metadata, invalid_reason: validation.reason },
          })
      ),
      { concurrency: 5 }
    );
    logger.info('Promotion codes marked as inactive', {
      promotionCodeIds: promotionCodes.map((pc) => pc.id),
      reason: validation.reason,
    });
  }
}

/**
 * Process promotion code status check
 */
async function processPromotionCodeStatus(promotionCodeId: string) {
  const promotionCode = await PromotionCode.findByPk(promotionCodeId);
  if (!promotionCode) {
    logger.warn('Promotion code not found for status check', { promotionCodeId });
    return;
  }

  // Skip if already inactive
  if (!promotionCode.active) {
    logger.info('Promotion code is already inactive, skipping', { promotionCodeId });
    return;
  }

  // Use the existing validPromotionCode function to check validity
  const validation = await validPromotionCode(promotionCode, {});

  if (!validation.valid) {
    // Mark promotion code as inactive
    await promotionCode.update({
      active: false,
      metadata: { ...promotionCode.metadata, invalid_reason: validation.reason },
    });
    logger.info('Promotion code marked as inactive', {
      promotionCodeId,
      reason: validation.reason,
      expires_at: promotionCode.expires_at,
      max_redemptions: promotionCode.max_redemptions,
      times_redeemed: promotionCode.times_redeemed,
    });
  }
}

/**
 * Schedule discount status monitoring jobs
 */
export async function addDiscountStatusJob(
  record: Coupon | PromotionCode,
  type: DiscountType = 'coupon',
  replace: boolean = false,
  options: { delay?: number; runAt?: number } = {}
) {
  const { id } = record;
  const jobId = `discount-status-${type}-${id}`;

  // Remove existing job if present
  const existingJob = await discountStatusQueue.get(jobId);
  if (existingJob && !replace) {
    logger.info('Discount job already exists, skipping', { type, id, jobId });
    return;
  }
  if (existingJob && replace) {
    await discountStatusQueue.delete(jobId);
    logger.info('Removed existing discount status job for update', { type, id, jobId });
  }
  discountStatusQueue.push({ id: jobId, job: { type, id }, ...options });
}

// Create unified discount status queue
export const discountStatusQueue = createQueue<DiscountStatusJobData>({
  name: 'discount-status',
  onJob: processDiscountStatus,
  options: {
    concurrency: 5,
    maxRetries: 3,
    enableScheduledJob: true,
  },
});

export const startDiscountStatusQueue = async () => {
  logger.info('Starting discount status queue...');

  const coupons = await Coupon.findAll({ where: { valid: true } });
  const promotionCodes = await PromotionCode.findAll({ where: { active: true } });
  await pAll(
    coupons.map(
      (coupon) => () =>
        addDiscountStatusJob(coupon, 'coupon', false, {
          runAt: coupon.redeem_by,
        })
    ),
    { concurrency: 5 }
  );
  await pAll(
    promotionCodes.map(
      (promotionCode) => () =>
        addDiscountStatusJob(promotionCode, 'promotion-code', false, {
          runAt: promotionCode.expires_at,
        })
    ),
    { concurrency: 5 }
  );
};

events.on('coupon.created', (coupon: Coupon) => {
  logger.info('Received coupon.created event', { couponId: coupon.id });
  addDiscountStatusJob(coupon, 'coupon', true, {
    runAt: coupon.redeem_by,
  });
});

events.on('promotion-code.created', (promotionCode: PromotionCode) => {
  addDiscountStatusJob(promotionCode, 'promotion-code', true, {
    runAt: promotionCode.expires_at,
  });
});

events.on('promotion-code.updated', (promotionCode: PromotionCode) => {
  if (promotionCode.active) {
    addDiscountStatusJob(promotionCode, 'promotion-code', true, {
      runAt: promotionCode.expires_at,
    });
  }
});

events.on('discount-status.queued', (record, type, replace) => {
  try {
    addDiscountStatusJob(record, type, replace);
    events.emit('discount-status.queued.done', record, type, replace);
  } catch (error) {
    logger.error('Error in discount-status.queued', { recordId: record.id, type, replace });
    events.emit('discount-status.queued.error', record, type, replace);
  }
});
