import { joinURL } from 'ufo';
import { Auth as VendorAuth } from '@blocklet/payment-vendor';
import createQueue from '../../libs/queue';
import { CheckoutSession } from '../../store/models/checkout-session';
import { ProductVendor } from '../../store/models';
import { fulfillmentCoordinatorQueue } from './fulfillment-coordinator';
import logger from '../../libs/logger';
import { vendorTimeoutMinutes } from '../../libs/env';
import { VendorFulfillmentService } from '../../libs/vendor-util/fulfillment';

export const startVendorStatusCheckSchedule = async () => {
  const checkoutSessions = await CheckoutSession.findAll({
    where: { fulfillment_status: 'sent' },
  });

  if (!checkoutSessions.length) {
    return;
  }

  for (const checkoutSession of checkoutSessions) {
    const vendorInfo = checkoutSession.vendor_info;
    if (!vendorInfo?.length) {
      return;
    }

    for (const vendor of vendorInfo) {
      if (vendor.status === 'sent') {
        vendorStatusCheckQueue.push({
          id: `vendor-status-check-${checkoutSession.id}-${vendor.vendor_id}`,
          job: {
            checkoutSessionId: checkoutSession.id,
            vendorId: vendor.vendor_id,
          },
        });
      }
    }
  }
};

export const handleVendorStatusCheck = async (job: VendorStatusCheckJob) => {
  const { checkoutSessionId, vendorId } = job;
  logger.info('handleVendorStatusCheck', { checkoutSessionId, vendorId });

  const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);

  const vendor = checkoutSession?.vendor_info?.find((v) => v.vendor_id === vendorId);
  if (!vendor) {
    logger.error('vendor not found[handleVendorStatusCheck]', { checkoutSessionId, vendorId });
    return { status: 'fail' };
  }

  const TIMEOUT_THRESHOLD = vendorTimeoutMinutes * 60 * 1000;
  const now = Date.now();

  if (vendor.lastAttemptAt) {
    const timeSinceLastAttempt = now - new Date(vendor.lastAttemptAt).getTime();

    if (timeSinceLastAttempt > TIMEOUT_THRESHOLD) {
      logger.warn('Vendor timeout detected during status check', {
        checkoutSessionId,
        vendorId,
        status: vendor.status,
        timeSinceLastAttempt: `${Math.floor(timeSinceLastAttempt / 1000)}s`,
        timeoutThreshold: `${vendorTimeoutMinutes}min (${Math.floor(TIMEOUT_THRESHOLD / 1000)}s)`,
      });

      vendor.status = 'failed';
      vendor.error_message = `Timeout: ${vendor.status} status exceeded ${vendorTimeoutMinutes} minutes`;
      vendor.lastAttemptAt = new Date().toISOString();

      const updatedVendorInfo = checkoutSession?.vendor_info?.map((v) => {
        if (v.vendor_id === vendorId) {
          return { ...v, ...vendor };
        }
        return v;
      });

      await CheckoutSession.update({ vendor_info: updatedVendorInfo }, { where: { id: checkoutSessionId } });

      fulfillmentCoordinatorQueue.push({
        id: `fulfillment-coordinator-${checkoutSessionId}-${vendorId}`,
        job: {
          checkoutSessionId,
          invoiceId: checkoutSession?.invoice_id || '',
          triggeredBy: 'vendor-status-check-timeout',
        },
      });

      return { status: 'failed', reason: 'timeout' };
    }
  }

  try {
    if (!vendor.app_url) {
      const productVendor = await ProductVendor.findByPk(vendorId);
      const url = productVendor?.app_url;
      logger.info('found vendor url', { url, productVendor });

      if (url) {
        const serverStatusUrl = joinURL(
          url,
          productVendor?.metadata?.mountPoint,
          '/api/vendor/status',
          vendor.order_id
        );
        const { headers } = await VendorAuth.signRequestWithHeaders({});

        const result = await fetch(serverStatusUrl, { headers });
        const data = await result.json();

        vendor.app_url = data?.appUrl || '';
      }
    }

    const adapter = await VendorFulfillmentService.getVendorAdapter(vendor.vendor_key);
    const result = await adapter.checkOrderStatus({ appUrl: vendor.app_url });

    if (result.status === 'completed') {
      vendor.status = 'completed';
      vendor.lastAttemptAt = new Date().toISOString();
    } else if (result.status === 'failed') {
      vendor.status = 'failed';
      vendor.lastAttemptAt = new Date().toISOString();
    } else if (result.status === 'processing') {
      vendor.status = 'processing';
      vendor.lastAttemptAt = new Date().toISOString();
    }
  } catch (error) {
    logger.warn('Failed to check vendor status', {
      checkoutSessionId,
      vendorId,
      error: error.message,
    });
  }

  const vendorInfo = checkoutSession?.vendor_info?.map((v) => {
    if (v.vendor_id === vendorId) {
      v.app_url = vendor.app_url || '';
      vendor.app_url = vendor.app_url || '';
      vendor.status = vendor.status || 'sent';
    }
    return v;
  });

  logger.info('update vendor info', { vendorInfo });

  await CheckoutSession.update(
    {
      vendor_info: vendorInfo,
    },
    { where: { id: checkoutSessionId } }
  );

  if (vendor.status === 'completed' || vendor.status === 'failed') {
    fulfillmentCoordinatorQueue.push({
      id: `fulfillment-coordinator-${checkoutSessionId}-${vendorId}`,
      job: {
        checkoutSessionId,
        invoiceId: checkoutSession?.invoice_id || '',
        triggeredBy: 'vendor-status-check',
      },
    });
  }

  return { status: vendor.status };
};

type VendorStatusCheckJob = {
  checkoutSessionId: string;
  vendorId: string;
};

export const vendorStatusCheckQueue = createQueue<VendorStatusCheckJob>({
  name: 'vendor-status-check',
  onJob: handleVendorStatusCheck,
  options: {
    concurrency: 1,
    maxRetries: 3,
    enableScheduledJob: true,
  },
});
