import Cron from '@abtnode/cron';

import { checkStakeRevokeTx } from '../integrations/arcblock/stake';
import { runIapReconcile } from '../integrations/iap-reconcile';
import {
  batchHandleStripeInvoices,
  batchHandleStripePayments,
  batchHandleStripeSubscriptions,
} from '../integrations/stripe/resource';
import {
  creditConsumptionCronTime,
  depositVaultCronTime,
  eventRetryCronTime,
  expiredSessionCleanupCronTime,
  iapReconcileCronTime,
  notificationCronTime,
  overdueDetectionCronTime,
  paymentStatCronTime,
  revokeStakeCronTime,
  stripeInvoiceCronTime,
  stripePaymentCronTime,
  stripeSubscriptionCronTime,
  subscriptionCronTime,
  vendorStatusCheckCronTime,
  vendorReturnScanCronTime,
} from '../libs/env';
import logger from '../libs/logger';
import { startCreditConsumeQueue } from '../queues/credit-consume';
import { startDepositVaultQueue } from '../queues/payment';
import { startRefundQueue } from '../queues/refund';
import { startSubscriptionQueue } from '../queues/subscription';
import { startVendorStatusCheckSchedule } from '../queues/vendors/status-check';
import { CheckoutSession } from '../store/models';
import { createOverdueDetection } from './overdue-detection';
import { createPaymentStat } from './payment-stat';
import { retryPendingEvents } from './retry-pending-events';
import { SubscriptionTrialWillEndSchedule } from './subscription-trial-will-end';
import { SubscriptionWillCanceledSchedule } from './subscription-will-canceled';
import { SubscriptionWillRenewSchedule } from './subscription-will-renew';
import { scheduleVendorReturnScan } from '../queues/vendors/return-scanner';
import { getRetentionConfig } from '../libs/archive/config';
import { recoverFromCrash } from '../libs/archive/executor';
import { enqueueArchiveJob } from '../queues/archive';

function init() {
  // Recover any stale archive jobs from previous crash/restart
  recoverFromCrash().catch((error) => {
    logger.error('archive crash recovery error during init', { error });
  });

  const retentionConfig = getRetentionConfig();
  const archiveJobs =
    retentionConfig.enabled && retentionConfig.schedule.enabled
      ? [
          {
            name: 'data.retention.archive',
            time: retentionConfig.schedule.cron,
            fn: () =>
              enqueueArchiveJob({
                triggeredBy: 'cron',
              }),
            options: { runOnInit: false },
          },
        ]
      : [];
  Cron.init({
    context: {},
    jobs: [
      {
        name: 'subscription.will.renew',
        time: notificationCronTime,
        fn: () => new SubscriptionWillRenewSchedule().run(),
        options: { runOnInit: true },
      },
      {
        name: 'subscription.trial.will.end',
        time: notificationCronTime,
        fn: () => new SubscriptionTrialWillEndSchedule().run(),
        options: { runOnInit: true },
      },
      {
        name: 'customer.subscription.will_canceled',
        time: notificationCronTime,
        fn: () => new SubscriptionWillCanceledSchedule().run(),
        options: { runOnInit: true },
      },
      {
        name: 'subscription.schedule.retry',
        time: subscriptionCronTime,
        fn: startSubscriptionQueue,
        options: { runOnInit: false },
      },
      {
        // Recover pending refunds that were missed (e.g. after deploy or failed dispatch).
        // Previously invoked unconditionally on every CF scheduled tick; now throttled
        // to every 5 minutes to stay within CF Queue free-tier ops budget.
        name: 'refund.recovery',
        time: '0 */5 * * * *',
        fn: startRefundQueue,
        options: { runOnInit: false },
      },
      {
        name: 'checkoutSession.cleanup.expired',
        time: expiredSessionCleanupCronTime,
        fn: async () => {
          const removedCount = await CheckoutSession.cleanupExpiredSessions();
          logger.info('CheckoutSession.cleanupExpiredSessions', { removedCount });
        },
        options: { runOnInit: true },
      },
      {
        name: 'stripe.invoice.sync',
        time: stripeInvoiceCronTime,
        fn: batchHandleStripeInvoices,
        options: { runOnInit: false },
      },
      {
        name: 'stripe.payment.sync',
        time: stripePaymentCronTime,
        fn: batchHandleStripePayments,
        options: { runOnInit: false },
      },
      {
        name: 'stripe.subscription.sync',
        time: stripeSubscriptionCronTime,
        fn: batchHandleStripeSubscriptions,
        options: { runOnInit: false },
      },
      {
        name: 'customer.stake.revoked',
        time: revokeStakeCronTime,
        fn: checkStakeRevokeTx,
        options: { runOnInit: false },
      },
      {
        // Backup for App Store / Google Play webhooks — pulls authoritative
        // subscription state and patches local drift (refunds/renewals the
        // webhook missed). See blocklets/core/api/src/integrations/iap-reconcile.ts.
        name: 'iap.reconcile',
        time: iapReconcileCronTime,
        fn: () => runIapReconcile(),
        options: { runOnInit: false },
      },
      {
        // Backstop for the fire-and-forget event → webhook path. Rescans events
        // with pending_webhooks>0 older than 60s and re-invokes handleEvent.
        // See blocklets/core/api/src/crons/retry-pending-events.ts.
        name: 'event.retry',
        time: eventRetryCronTime,
        fn: () => retryPendingEvents(),
        options: { runOnInit: false },
      },
      {
        name: 'payment.stat',
        time: paymentStatCronTime,
        fn: () => createPaymentStat(),
        options: { runOnInit: false },
      },
      {
        name: 'payment.daily.report',
        time: overdueDetectionCronTime,
        fn: () => createOverdueDetection(),
        options: { runOnInit: false },
      },
      {
        name: 'deposit.vault',
        time: depositVaultCronTime,
        fn: () => startDepositVaultQueue(),
        options: { runOnInit: true },
      },
      {
        name: 'credit.consumption',
        time: creditConsumptionCronTime,
        fn: () => startCreditConsumeQueue(),
        options: { runOnInit: true },
      },
      {
        name: 'vendor.status.check',
        time: vendorStatusCheckCronTime,
        fn: () => startVendorStatusCheckSchedule(),
        options: { runOnInit: false },
      },
      {
        name: 'vendor.return.scan',
        time: vendorReturnScanCronTime,
        fn: () => scheduleVendorReturnScan(),
        options: { runOnInit: false },
      },
      ...archiveJobs,
    ],
    onError: (error: Error, name: string) => {
      logger.error('run job failed', { name, error });
    },
  });
}

export default {
  init,
};
