/* eslint-disable @typescript-eslint/brace-style */
/* eslint-disable @typescript-eslint/indent */
import pWaitFor from 'p-wait-for';
import isEmpty from 'lodash/isEmpty';

import { getPaymentAmountForCycleSubscription } from '../../subscription';
import { translate } from '../../../locales';
import { CheckoutSession, NftMintItem, PaymentIntent, Invoice, NFTMintChainType } from '../../../store/models';
import { getOneTimeProductInfo } from '../../invoice';
import { formatTime } from '../../time';
import { formatCurrencyInfo, getExplorerLink } from '../../util';
import { BaseSubscriptionEmailTemplate, BaseEmailTemplateType } from './base';

export interface SubscriptionSucceededEmailTemplateOptions {
  subscriptionId: string;
}

interface OneTimeProductInfo {
  paymentInfo: string;
  productName: string;
  quantity: number;
}

interface SubscriptionSucceededEmailTemplateContext {
  locale: string;
  productName: string;
  at: string;
  nftMintItem: NftMintItem | undefined;
  chainHost: string | undefined;
  userDid: string;
  paymentInfo: string;
  currentPeriodStart: string;
  currentPeriodEnd: string;
  duration: string;
  viewSubscriptionLink: string;
  viewInvoiceLink: string;
  viewTxHashLink: string | undefined;
  oneTimeProductInfo?: Array<OneTimeProductInfo>;
  customActions: any[];
  isCreditSubscription: boolean;
}

export class SubscriptionSucceededEmailTemplate extends BaseSubscriptionEmailTemplate<SubscriptionSucceededEmailTemplateContext> {
  options: SubscriptionSucceededEmailTemplateOptions;

  constructor(options: SubscriptionSucceededEmailTemplateOptions) {
    super();
    this.options = options;
  }

  async getContext(): Promise<SubscriptionSucceededEmailTemplateContext> {
    // 获取基础订阅数据
    const basicData = await this.getSubscriptionBasicData(this.options.subscriptionId);
    const { subscription, userDid, locale, productName, paymentCurrency, isCreditSubscription } = basicData;

    if (subscription.status !== 'active') {
      throw new Error(`Subscription not active: ${this.options.subscriptionId}`);
    }

    // 等待相关数据准备就绪
    await pWaitFor(
      async () => {
        const invoice = await Invoice.findOne({
          where: { subscription_id: subscription.id },
          order: [['created_at', 'ASC']],
        });
        const checkoutSession = await CheckoutSession.findBySubscriptionId(subscription.id);

        return Boolean(
          ['disabled', 'minted', 'sent', 'error'].includes(checkoutSession?.nft_mint_status as string) &&
          (invoice?.payment_intent_id || (invoice && +invoice.amount_remaining === 0))
        );
      },
      { timeout: 1000 * 10, interval: 1000 }
    );

    const invoice = (await Invoice.findByPk(subscription.latest_invoice_id)) as Invoice;

    // 获取支付信息
    const paymentInfoResult = await this.getPaymentInfo(
      subscription,
      paymentCurrency,
      isCreditSubscription,
      invoice?.id
    );

    // 获取链接
    const links = this.generateSubscriptionLinks(
      subscription,
      locale,
      userDid,
      'customer.subscription.started',
      invoice?.id,
      isCreditSubscription
    );

    // 获取 NFT 信息
    const nftInfo = await this.getNftMintInfo(subscription.id);

    // 获取一次性产品信息
    const oneTimeProductInfo = await getOneTimeProductInfo(subscription.latest_invoice_id as string, paymentCurrency);

    // 计算支付金额和时间信息
    const paymentAmount = await getPaymentAmountForCycleSubscription(subscription, paymentCurrency);
    const formattedPaymentInfo = formatCurrencyInfo(
      paymentAmount,
      paymentCurrency,
      paymentInfoResult.paymentMethod,
      true
    );

    const periodInfo = this.formatSubscriptionPeriod(
      subscription.current_period_start || invoice.period_start,
      subscription.current_period_end || invoice.period_end,
      locale
    );

    // 获取交易哈希链接
    const checkoutSession = await CheckoutSession.findBySubscriptionId(subscription.id);
    const paymentIntent = await PaymentIntent.findByPk(invoice.payment_intent_id);
    const txHash: string | undefined =
      paymentIntent?.payment_details?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]?.tx_hash;

    const chainHost: string | undefined =
      paymentInfoResult.paymentMethod?.settings?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]
        ?.api_host;

    const viewTxHashLink: string | undefined = txHash && getExplorerLink({ type: 'tx', did: txHash, chainHost });

    return {
      locale,
      productName,
      at: formatTime(subscription.created_at),
      userDid,
      nftMintItem: nftInfo.nftMintItem,
      chainHost,
      paymentInfo: formattedPaymentInfo,
      currentPeriodStart: periodInfo.currentPeriodStart,
      currentPeriodEnd: periodInfo.currentPeriodEnd,
      duration: periodInfo.duration,
      viewSubscriptionLink: links.viewSubscriptionLink,
      viewInvoiceLink: links.viewInvoiceLink!,
      viewTxHashLink,
      oneTimeProductInfo,
      customActions: links.customActions,
      isCreditSubscription,
    };
  }

  private getOneTimeProductTemplate(oneTimeProductInfo: Array<OneTimeProductInfo>, locale: string = 'en') {
    return [
      { type: 'divider' },
      {
        type: 'text',
        data: {
          type: 'plain',
          text: translate('notification.common.expandPayment', locale),
        },
      },
      {
        type: 'section',
        fields: (oneTimeProductInfo || []).flatMap((x: any) => [
          {
            type: 'text',
            data: {
              type: 'plain',
              color: '#9397A1',
              text: translate('notification.common.product', locale),
            },
          },
          {
            type: 'text',
            data: { type: 'plain', text: x?.productName },
          },
          {
            type: 'text',
            data: {
              type: 'plain',
              color: '#9397A1',
              text: translate('notification.common.paymentAmount', locale),
            },
          },
          {
            type: 'text',
            data: { type: 'plain', text: x?.paymentInfo },
          },
          {
            type: 'text',
            data: {
              type: 'plain',
              color: '#9397A1',
              text: translate('notification.common.paymentQuantity', locale),
            },
          },
          {
            type: 'text',
            data: {
              type: 'plain',
              text: translate('notification.common.qty', locale, { count: x.quantity }),
            },
          },
        ]),
      },
    ];
  }

  async getTemplate(): Promise<BaseEmailTemplateType> {
    const context = await this.getContext();
    const {
      locale,
      productName,
      at,
      nftMintItem,
      chainHost,
      userDid,
      paymentInfo,
      currentPeriodStart,
      currentPeriodEnd,
      duration,
      viewSubscriptionLink,
      viewInvoiceLink,
      viewTxHashLink,
      oneTimeProductInfo,
      customActions,
      isCreditSubscription,
    } = context;

    const hasOneTimeProduct = !isEmpty(oneTimeProductInfo);

    // 构建基础字段
    const commonFields = this.buildCommonFields(userDid, productName, locale);
    const paymentFields = this.buildPaymentField(paymentInfo, locale, isCreditSubscription);
    const periodFields = this.buildPeriodField(
      currentPeriodStart,
      currentPeriodEnd,
      duration,
      locale,
      isCreditSubscription
    );

    // 构建操作按钮
    const actions = this.buildCommonActions(viewSubscriptionLink, viewInvoiceLink, locale, isCreditSubscription, [
      viewTxHashLink && {
        name: translate('notification.common.viewTxHash', locale),
        title: translate('notification.common.viewTxHash', locale),
        link: viewTxHashLink as string,
      },
      ...customActions,
    ]);

    const template: BaseEmailTemplateType = {
      title: translate('notification.subscriptionSucceed.title', locale, { productName }),
      body: translate('notification.subscriptionSucceed.body', locale, { at, productName }),
      // @ts-expect-error
      attachments: [
        nftMintItem &&
          chainHost && {
            type: 'asset',
            data: { chainHost, did: nftMintItem.address },
          },
        hasOneTimeProduct && {
          type: 'text',
          data: {
            type: 'plain',
            text: translate('notification.common.subscribeProduct', locale),
          },
        },
        {
          type: 'section',
          fields: [...commonFields, ...paymentFields, ...periodFields].filter(Boolean),
        },
        ...(hasOneTimeProduct
          ? this.getOneTimeProductTemplate(oneTimeProductInfo as Array<OneTimeProductInfo>, locale)
          : []),
      ].filter(Boolean),
      // @ts-ignore
      actions: actions.filter(Boolean),
    };

    return template;
  }
}
