import logger from '../../libs/logger';
import createQueue from '../../libs/queue';
import { VendorFulfillmentService } from '../../libs/vendor-util/fulfillment';
import { buildRefundInfoFromPayout } from '../../libs/vendor-util/tool';
import { CheckoutSession, Payout } from '../../store/models';
import { payoutQueue } from '../payout';
import { VendorInfo } from './fulfillment-coordinator';

export const MAX_RETURN_RETRY = 3;

type ReturnProcessorJob = {
  checkoutSessionId: string;
};

export const vendorReturnProcessorQueue = createQueue<ReturnProcessorJob>({
  name: 'vendor-return-processor',
  onJob: handleReturnProcessorJob,
  options: {
    concurrency: 1,
    maxRetries: 2,
    retryDelay: 30 * 1000,
  },
});

async function handleReturnProcessorJob(job: ReturnProcessorJob): Promise<void> {
  const { checkoutSessionId } = job;

  logger.info('Starting vendor return processor job', {
    checkoutSessionId,
  });

  try {
    const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);

    if (!checkoutSession) {
      logger.warn('CheckoutSession not found', { checkoutSessionId });
      return;
    }

    const vendorInfoList = checkoutSession.vendor_info as VendorInfo[];

    if (!vendorInfoList || vendorInfoList.length === 0) {
      logger.warn('No vendor info found', { checkoutSessionId });
      return;
    }

    let hasChanges = false;

    let i = -1;
    for (const vendor of vendorInfoList) {
      i++;
      const returnRetry = vendor.returnRetry ? vendor.returnRetry + 1 : 1;
      if (vendor.status === 'returned') {
        logger.info('Skipping vendor return because status is returned', {
          checkoutSessionId,
          vendorId: vendor.vendor_id,
          orderId: vendor.order_id,
          status: vendor.status,
          returnRetry,
        });
        // eslint-disable-next-line no-continue
        continue;
      }

      try {
        logger.info('Processing vendor return', {
          checkoutSessionId,
          vendorId: vendor.vendor_id,
          orderId: vendor.order_id,
          returnRetry,
        });

        // eslint-disable-next-line no-await-in-loop
        await callVendorReturn(vendor, checkoutSession);

        // Return successful, update status to 'returned'
        vendorInfoList[i] = {
          ...vendor,
          status: 'returned',
          lastAttemptAt: new Date().toISOString(),
        };
        hasChanges = true;

        logger.info('Vendor return successful', {
          checkoutSessionId,
          vendorId: vendor.vendor_id,
          orderId: vendor.order_id,
          returnRetry,
        });
      } catch (error: any) {
        logger.error('Error processing vendor return', {
          checkoutSessionId,
          vendorId: vendor.vendor_id,
          orderId: vendor.order_id,
          error,
          returnRetry,
        });

        if (returnRetry >= MAX_RETURN_RETRY) {
          logger.warn('Skipping vendor return because return retry is greater than 5', {
            checkoutSessionId,
            vendorId: vendor.vendor_id,
            orderId: vendor.order_id,
            returnRetry,
          });
        }

        // Record error but keep status unchanged for retry
        vendorInfoList[i] = {
          ...vendor,
          status: returnRetry >= MAX_RETURN_RETRY ? 'returned' : vendor.status,
          lastAttemptAt: new Date().toISOString(),
          error_message: error.message,
          returnRetry,
        };
        hasChanges = true;
      }
    }

    // Update vendor_info if there are changes
    if (hasChanges) {
      await CheckoutSession.update({ vendor_info: vendorInfoList }, { where: { id: checkoutSessionId } });
    }

    // Check if all vendors have been returned
    const allReturned = vendorInfoList.every((vendor) => vendor.status === 'returned');

    if (allReturned && checkoutSession.fulfillment_status !== 'returned') {
      await CheckoutSession.update({ fulfillment_status: 'returned' }, { where: { id: checkoutSessionId } });

      logger.info('All vendors returned, updated fulfillment status to returned', {
        checkoutSessionId,
        totalVendors: vendorInfoList.length,
      });
    }

    logger.info('Vendor return processor job completed', {
      checkoutSessionId,
      totalVendors: vendorInfoList.length,
      allReturned,
      hasChanges,
    });
  } catch (error: any) {
    logger.error('Vendor return processor job failed', {
      checkoutSessionId,
      error,
    });
    throw error;
  }
}

async function callVendorReturn(vendor: VendorInfo, checkoutSession: CheckoutSession) {
  const vendorAdapter = await VendorFulfillmentService.getVendorAdapter(vendor.vendor_key);

  if (!vendorAdapter) {
    throw new Error(`No adapter found for vendor: ${vendor.vendor_id}`);
  }

  const payoutInfo = await Payout.findOne({
    where: { vendor_info: { order_id: vendor.order_id, vendor_id: vendor.vendor_id } },
  });
  let refundInfo;
  if (payoutInfo) {
    if (['pending', 'deferred', 'failed'].includes(payoutInfo.status)) {
      logger.info('[callVendorReturn] Cancelling payout status to canceled', {
        checkoutSessionId: checkoutSession.id,
        vendorId: vendor.vendor_id,
        orderId: vendor.order_id,
      });
      await payoutQueue.delete(payoutInfo.id);
      await payoutInfo.update({ status: 'canceled' });
    } else if (payoutInfo.status === 'paid') {
      try {
        refundInfo = await buildRefundInfoFromPayout(payoutInfo);
      } catch (error: any) {
        logger.error('[callVendorReturn] Error building refund info', {
          checkoutSessionId: checkoutSession.id,
          vendorId: vendor.vendor_id,
          orderId: vendor.order_id,
          error,
        });
        throw error;
      }
      logger.info('[callVendorReturn] Need to request vendor payout refund', {
        checkoutSessionId: checkoutSession.id,
        vendorId: vendor.vendor_id,
        orderId: vendor.order_id,
        refundInfo,
      });
    }
  } else {
    logger.error('[callVendorReturn] No payout info found', {
      checkoutSessionId: checkoutSession.id,
      vendorId: vendor.vendor_id,
      orderId: vendor.order_id,
    });
  }

  try {
    const result = await vendorAdapter.requestReturn({
      orderId: vendor.order_id,
      reason: 'Subscription canceled',
      customParams: {
        checkoutSessionId: checkoutSession.id,
        subscriptionId: checkoutSession.subscription_id,
        vendorKey: vendor.vendor_key,
        refundInfo,
      },
    });

    if (payoutInfo && result.data?.refundResult?.status === 'succeeded') {
      logger.info('[callVendorReturn] Vendor payout refund successful', {
        checkoutSessionId: checkoutSession.id,
        vendorId: vendor.vendor_id,
        orderId: vendor.order_id,
      });
      await payoutInfo.update({ status: 'reverted' });
    } else {
      logger.error('[callVendorReturn] Vendor payout refund failed', {
        checkoutSessionId: checkoutSession.id,
        vendorId: vendor.vendor_id,
        orderId: vendor.order_id,
        refundInfo,
        refundResult: result.data?.refundResult,
      });
    }

    return result;
  } catch (error: any) {
    logger.error('[callVendorReturn] Error requesting return', {
      checkoutSessionId: checkoutSession.id,
      vendorId: vendor.vendor_id,
      orderId: vendor.order_id,
      error,
    });
    throw error;
  }
}
