import type { CallbackArgs } from '../../libs/auth';
import { PaymentMethod, type TLineItemExpanded } from '../../store/models';
import { ensureReStakeContext, executeOcapTransactions, getAuthPrincipalClaim, getStakeTxClaim } from './shared';
import { ensureStakeInvoice } from '../../libs/invoice';
import logger from '../../libs/logger';
import { addSubscriptionJob, subscriptionQueue } from '../../queues/subscription';
import { SubscriptionWillCanceledSchedule } from '../../crons/subscription-will-canceled';

export default {
  action: 're-stake',
  authPrincipal: false,
  persistentDynamicClaims: false,
  claims: {
    authPrincipal: async ({ extraParams }: CallbackArgs) => {
      const { paymentMethod } = await ensureReStakeContext(extraParams.subscriptionId);
      return getAuthPrincipalClaim(paymentMethod, 'continue');
    },
  },
  onConnect: async ({ userDid, userPk, extraParams }: CallbackArgs) => {
    const { subscriptionId } = extraParams;
    const { subscription, paymentMethod, paymentCurrency, payerAddress } = await ensureReStakeContext(subscriptionId);
    if (userDid !== payerAddress) {
      throw new Error(
        `You are not the payer for this subscription. Expected payer: ${payerAddress}, but found: ${userDid}.`
      );
    }
    // @ts-ignore
    const items = subscription!.items as TLineItemExpanded[];

    if (paymentMethod.type === 'arcblock') {
      return [
        {
          prepareTx: await getStakeTxClaim({
            userDid,
            userPk,
            paymentCurrency,
            paymentMethod,
            items,
            subscription,
          }),
        },
      ];
    }

    throw new Error(`Payment method ${paymentMethod.type} not supported`);
  },

  onAuth: async ({ request, userDid, userPk, claims, extraParams }: CallbackArgs) => {
    const { subscriptionId } = extraParams;
    const { subscription, paymentMethod, paymentCurrency, customer } = await ensureReStakeContext(subscriptionId);

    if (paymentMethod.type === 'arcblock') {
      const { stakingAmount, ...paymentDetails } = await executeOcapTransactions(
        userDid,
        userPk,
        claims,
        paymentMethod,
        request,
        subscription?.id,
        paymentCurrency.contract
      );

      // 创建质押账单
      await ensureStakeInvoice(
        {
          total: stakingAmount,
          description: 'Re-stake to resume subscription',
          currency_id: paymentCurrency.id,
          metadata: {
            payment_details: {
              arcblock: {
                tx_hash: paymentDetails?.staking?.tx_hash,
                payer: paymentDetails?.payer,
                address: paymentDetails?.staking?.address,
              },
            },
          },
        },
        subscription!,
        paymentMethod,
        customer!
      );

      if (subscription.payment_details?.stripe?.subscription_id) {
        const method = await PaymentMethod.findOne({ where: { type: 'stripe', livemode: subscription.livemode } });
        if (method) {
          const client = method.getStripeClient();
          await client.subscriptions.update(subscription.payment_details.stripe.subscription_id, {
            cancel_at_period_end: false,
          });
          await client.subscriptions.update(subscription.payment_details.stripe.subscription_id, {
            cancel_at: null,
          });
        }
      }
      await subscription.update({
        cancel_at_period_end: false,
        cancel_at: 0,
        canceled_at: 0,
        // @ts-ignore
        cancelation_details: null,
        payment_details: {
          ...subscription.payment_details,
          [paymentMethod.type]: paymentDetails,
        },
      });

      await new SubscriptionWillCanceledSchedule().deleteScheduleSubscriptionJobs([subscription]);

      subscriptionQueue
        .delete(`cancel-${subscription.id}`)
        .then(() => logger.info('subscription cancel job is canceled'))
        .catch((err) => logger.error('subscription cancel job failed to cancel', { error: err }));
      await addSubscriptionJob(subscription, 'cycle');

      logger.info('Subscription resumed with re-stake', {
        subscriptionId: subscription.id,
        stakingTxHash: paymentDetails?.staking?.tx_hash,
        stakingAddress: paymentDetails?.staking?.address,
      });
      return {
        hash: paymentDetails.staking?.tx_hash,
      };
    }

    throw new Error(`Payment method ${paymentMethod.type} not supported`);
  },
};
