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

import { getUserLocale } from '../../../integrations/blocklet/notification';
import { translate } from '../../../locales';
import { CheckoutSession, Customer, NftMintItem, PaymentIntent, PaymentMethod } from '../../../store/models';
import { PaymentCurrency } from '../../../store/models/payment-currency';
import { getMainProductNameByCheckoutSession } from '../../product';
import { formatTime } from '../../time';
import { formatCurrencyInfo, getExplorerLink } from '../../util';
import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
import { getCustomerInvoicePageUrl } from '../../invoice';

export interface OneTimePaymentSucceededEmailTemplateOptions {
  checkoutSessionId: string;
}

interface OneTimePaymentSucceededEmailTemplateContext {
  locale: string;
  productName: string;
  at: string;

  nftMintItem: NftMintItem | undefined;
  chainHost: string | undefined;
  userDid: string;
  paymentInfo: string;

  viewInvoiceLink: string;
  viewTxHashLink: string | undefined;
}

/**
 * @see https://github.com/blocklet/payment-kit/issues/236#issue-2007698930
 * @description
 * @export
 * @class OneTimePaymentSucceededEmailTemplate
 * @implements {BaseEmailTemplate<OneTimePaymentSucceededEmailTemplateContext>}
 */
export class OneTimePaymentSucceededEmailTemplate implements BaseEmailTemplate<OneTimePaymentSucceededEmailTemplateContext> {
  options: OneTimePaymentSucceededEmailTemplateOptions;

  constructor(options: OneTimePaymentSucceededEmailTemplateOptions) {
    this.options = options;
  }

  async getContext(): Promise<OneTimePaymentSucceededEmailTemplateContext> {
    const cs: CheckoutSession | null = await CheckoutSession.findByPk(this.options.checkoutSessionId);
    if (!cs) {
      throw new Error(`CheckoutSession(${this.options.checkoutSessionId}) not found`);
    }
    if (cs.mode !== 'payment') {
      throw new Error(`CheckoutSession(${this.options.checkoutSessionId}) mode must be payment`);
    }

    const customer = await Customer.findByPk(cs.customer_id);
    if (!customer) {
      throw new Error(`Customer not found: ${cs.customer_id}`);
    }

    await pWaitFor(
      async () => {
        const checkoutSession = await CheckoutSession.findOne({
          where: {
            id: cs.id,
          },
        });

        return Boolean(['disabled', 'minted', 'sent', 'error'].includes(checkoutSession?.nft_mint_status as string));
      },
      { timeout: 1000 * 10, interval: 1000 }
    );

    const checkoutSession = (await CheckoutSession.findByPk(cs.id)) as CheckoutSession;

    const paymentCurrency = (await PaymentCurrency.findOne({
      where: {
        id: checkoutSession.currency_id,
      },
    })) as PaymentCurrency;

    const userDid: string = customer.did;
    const locale = await getUserLocale(userDid);
    const productName = await getMainProductNameByCheckoutSession(checkoutSession);
    const at: string = formatTime(checkoutSession.created_at);

    const hasNft: boolean = checkoutSession?.nft_mint_status === 'minted';
    const nftMintItem: NftMintItem | undefined = hasNft
      ? // @ts-expect-error
        checkoutSession?.nft_mint_details?.[paymentMethod.type]
      : undefined;

    const paymentIntent = await PaymentIntent.findByPk(checkoutSession!.payment_intent_id);
    const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(paymentIntent!.payment_method_id);

    const paymentInfo = formatCurrencyInfo(checkoutSession?.amount_total, paymentCurrency, paymentMethod);

    // @ts-expect-error
    const chainHost: string | undefined = paymentMethod?.settings?.[paymentMethod.type]?.api_host;
    const viewInvoiceLink = paymentIntent?.invoice_id
      ? getCustomerInvoicePageUrl({
          invoiceId: paymentIntent.invoice_id,
          userDid,
          locale,
        })
      : '';

    // @ts-expect-error
    const txHash: string | undefined = paymentIntent?.payment_details?.[paymentMethod.type]?.tx_hash;
    const viewTxHashLink: string | undefined =
      txHash &&
      getExplorerLink({
        type: 'tx',
        did: txHash,
        chainHost,
      });

    return {
      locale,
      productName,
      at,

      userDid,
      nftMintItem,
      chainHost,
      paymentInfo,

      viewInvoiceLink,
      viewTxHashLink,
    };
  }

  async getTemplate(): Promise<BaseEmailTemplateType> {
    const { locale, productName, at, nftMintItem, chainHost, userDid, paymentInfo, viewInvoiceLink, viewTxHashLink } =
      await this.getContext();

    const template: BaseEmailTemplateType = {
      title: `${translate('notification.oneTimePaymentSucceeded.title', locale, {
        productName,
      })}`,
      body: `${translate('notification.oneTimePaymentSucceeded.body', locale, {
        at,
        productName,
      })}`,
      // @ts-expect-error
      attachments: [
        nftMintItem &&
          chainHost && {
            type: 'asset',
            data: {
              chainHost,
              did: nftMintItem.address,
            },
          },
        {
          type: 'section',
          fields: [
            {
              type: 'text',
              data: {
                type: 'plain',
                color: '#9397A1',
                text: translate('notification.common.account', locale),
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                text: userDid,
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                color: '#9397A1',
                text: translate('notification.common.product', locale),
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                text: productName,
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                color: '#9397A1',
                text: translate('notification.common.paymentAmount', locale),
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                text: paymentInfo,
              },
            },
          ].filter(Boolean),
        },
      ].filter(Boolean),
      // @ts-ignore
      actions: [
        viewInvoiceLink && {
          name: translate('notification.common.viewInvoice', locale),
          title: translate('notification.common.viewInvoice', locale),
          link: viewInvoiceLink,
        },
        viewTxHashLink && {
          name: translate('notification.common.viewTxHash', locale),
          title: translate('notification.common.viewTxHash', locale),
          link: viewTxHashLink as string,
        },
      ].filter(Boolean),
    };

    return template;
  }
}
