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

import { getUrl } from '@blocklet/sdk';
import { getUserLocale } from '../../../integrations/blocklet/notification';
import { translate } from '../../../locales';
import {
  CheckoutSession,
  Customer,
  DonationSettings,
  NftMintItem,
  PaymentBeneficiary,
  PaymentIntent,
  PaymentLink,
  PaymentMethod,
} from '../../../store/models';
import { PaymentCurrency } from '../../../store/models/payment-currency';
import { getCustomerInvoicePageUrl } from '../../invoice';
import logger from '../../logger';
import { formatTime } from '../../time';
import {
  formatTokenAmount,
  getBlockletJson,
  getCustomerProfileUrl,
  getExplorerLink,
  getUserOrAppInfo,
} from '../../util';
import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';

export interface CustomerRewardSucceededEmailTemplateOptions {
  checkoutSessionId: string;
}

interface CustomerRewardSucceededEmailTemplateContext {
  locale: string;
  at: string;

  nftMintItem: NftMintItem | undefined;
  chainHost: string | undefined;
  userDid: string;
  paymentInfo: string;
  rewardDetail:
    | {
        url: string;
        title: string;
        appDID: string;
        logo?: string;
        desc?: string;
      }[]
    | null;
  donationSettings: DonationSettings;

  viewInvoiceLink: string;
  viewTxHashLink: string;
}

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

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

  async getContext(): Promise<CustomerRewardSucceededEmailTemplateContext> {
    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`);
    }

    let userDid = '';
    if (cs.customer_id) {
      const customer = await Customer.findByPk(cs.customer_id);
      if (customer) {
        userDid = customer.did;
      }
    }

    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;
    if (!checkoutSession.payment_link_id) {
      throw new Error(`Payment link cannot be found for checkoutSession: ${cs.id}`);
    }

    const paymentLink = await PaymentLink.findByPk(checkoutSession.payment_link_id);
    if (!paymentLink) {
      throw new Error(`Payment link cannot be found for payment_link_id(${checkoutSession.payment_link_id})`);
    }
    if (paymentLink.submit_type !== 'donate') {
      throw new Error(`Payment link submit_type(${paymentLink.submit_type}) must be donate`);
    }

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

    const paymentIntent = await PaymentIntent.findByPk(checkoutSession!.payment_intent_id);
    if (!paymentIntent) {
      throw new Error(
        `Payment intent cannot be found for checkoutSession.payment_intent_id${checkoutSession!.payment_intent_id}`
      );
    }

    // 如果没有从customer获取到userDid，尝试从支付详情中获取
    if (!userDid) {
      const paymentMethod = await PaymentMethod.findByPk(paymentIntent.payment_method_id);
      if (paymentMethod) {
        // @ts-expect-error
        userDid = paymentIntent.payment_details?.[paymentMethod.type]?.payer || '';
      }
    }

    if (!userDid) {
      throw new Error('User DID not found for reward notification');
    }

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

    const paymentInfo: string = `${formatTokenAmount(checkoutSession?.amount_total, paymentCurrency.decimal)} ${paymentCurrency.symbol}`;

    const rewardDetail = await this.getRewardDetail({
      paymentIntent,
      paymentCurrency,
      locale,
    });
    const donationSettings: DonationSettings = paymentLink.donation_settings as DonationSettings;

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

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

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

    return {
      locale,
      at,

      userDid,
      nftMintItem,
      chainHost,
      paymentInfo,
      rewardDetail,
      donationSettings,

      viewInvoiceLink,
      viewTxHashLink,
    };
  }
  async getRewardDetail({
    paymentIntent,
    paymentCurrency,
    locale,
  }: {
    paymentIntent: PaymentIntent;
    paymentCurrency: PaymentCurrency;
    locale: LiteralUnion<'zh' | 'en', string>;
  }): Promise<
    | {
        url: string;
        title: string;
        appDID: string;
        logo?: string;
        desc?: string;
      }[]
    | null
  > {
    if (isEmpty(paymentIntent.beneficiaries)) {
      logger.warn('Payment intent not available', { paymentIntentId: paymentIntent.id });
      return null;
    }

    const blockletJson = await getBlockletJson();
    const promises = paymentIntent.beneficiaries!.map((x: PaymentBeneficiary) => {
      return getUserOrAppInfo(x.address, blockletJson);
    });
    const users = await Promise.all(promises);
    if (!users.length) {
      logger.warn('No users found for payment intent', { paymentIntentId: paymentIntent.id });
      return null;
    }
    const rewardDetail = paymentIntent.beneficiaries!.map((x: PaymentBeneficiary, index: number) => {
      return {
        url: users[index]?.url || getCustomerProfileUrl({ userDid: x.address, locale }),
        title: translate('notification.customerRewardSucceeded.received', locale, {
          address: users[index]?.name || x.address,
          amount: `${formatTokenAmount(x.share, paymentCurrency.decimal)} ${paymentCurrency.symbol}`,
        }),
        logo: users[index]?.avatar ? users[index]?.avatar : getUrl('/methods/default.png'),
        appDID: x.address,
        desc: '',
      };
    });

    return rewardDetail;
  }

  async getTemplate(): Promise<BaseEmailTemplateType> {
    const {
      locale,
      at,
      nftMintItem,
      chainHost,
      userDid,
      paymentInfo,
      rewardDetail,
      donationSettings,

      viewInvoiceLink,
      viewTxHashLink,
    } = await this.getContext();

    const template: BaseEmailTemplateType = {
      title: `${translate('notification.customerRewardSucceeded.title', locale, {
        amount: paymentInfo,
      })}`,
      body: `${translate('notification.customerRewardSucceeded.body', locale, {
        at,
        amount: paymentInfo,
      })}`,
      // @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.rewardAmount', locale),
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                text: `${paymentInfo}`,
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                color: '#9397A1',
                text: translate('notification.common.rewardDetail', locale),
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                text: ' ', // TODO: ios bug fix, wait for next release
              },
            },
          ].filter(Boolean),
        },
        ...(rewardDetail
          ? rewardDetail.map((x) => ({
              type: 'dapp',
              data: x,
            }))
          : []),
      ].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,
        },
        donationSettings.reference && {
          name: translate('notification.customerRewardSucceeded.viewDetail', locale),
          title: translate('notification.customerRewardSucceeded.viewDetail', locale),
          link: donationSettings.reference,
        },
      ].filter(Boolean),
    };
    return template;
  }
}
