/* eslint-disable @typescript-eslint/brace-style */
/* eslint-disable @typescript-eslint/indent */
import { toDid } from '@ocap/util';
import camelCase from 'lodash/camelCase';
import prettyMsI18n from 'pretty-ms-i18n';

import { getUserLocale } from '../../../integrations/blocklet/notification';
import { translate } from '../../../locales';
import {
  CheckoutSession,
  Customer,
  NFTMintChainType,
  Invoice,
  NftMintItem,
  PaymentIntent,
  PaymentMethod,
  Subscription,
} from '../../../store/models';
import { PaymentCurrency } from '../../../store/models/payment-currency';
import { getCustomerInvoicePageUrl } from '../../invoice';
import { SufficientForPaymentResult, getPaymentDetail } from '../../payment';
import { getMainProductName } from '../../product';
import { getCustomerSubscriptionPageUrl, getSubscriptionPaymentAddress } from '../../subscription';
import { formatTime, getPrettyMsI18nLocale } from '../../time';
import { formatCurrencyInfo, getExplorerLink, getSubscriptionNotificationCustomActions } from '../../util';
import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';

export interface SubscriptionRenewFailedEmailTemplateOptions {
  invoice: Invoice;
  result: SufficientForPaymentResult;
}

interface SubscriptionRenewFailedEmailTemplateContext {
  locale: string;
  productName: string;
  at: string;
  reason: string;

  nftMintItem: NftMintItem | undefined;
  userDid: string;
  paymentInfo: string;
  currentPeriodStart: string;
  currentPeriodEnd: string;
  duration: string;

  viewSubscriptionLink: string;
  viewInvoiceLink: string;
  viewTxHashLink: string | undefined;
  customActions: any[];
  payer: string;
}

export class SubscriptionRenewFailedEmailTemplate implements BaseEmailTemplate<SubscriptionRenewFailedEmailTemplateContext> {
  options: SubscriptionRenewFailedEmailTemplateOptions;

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

  private async getReason(userDid: string, invoice: Invoice, locale: string): Promise<string> {
    if (this.options.result.sufficient) {
      throw new Error(`SufficientForPaymentResult.sufficient should be false: ${JSON.stringify(this.options.result)}`);
    }

    const i18nText = `notification.subscriptionRenewFailed.reason.${camelCase(this.options.result.reason as string)}`;

    const paymentDetail = await getPaymentDetail(userDid, invoice);
    const reason = translate(i18nText, locale, {
      balance: `${paymentDetail.balance} ${paymentDetail.symbol}`,
      price: `${paymentDetail.price} ${paymentDetail.symbol}`,
    });

    return reason;
  }

  async getContext(): Promise<SubscriptionRenewFailedEmailTemplateContext> {
    const subscription: Subscription | null = await Subscription.findByPk(this.options.invoice.subscription_id);
    if (!subscription) {
      throw new Error(`Subscription not found: ${this.options.invoice.subscription_id}`);
    }

    if (subscription.isImmutable()) {
      throw new Error(`Cannot renew an immutable subscription: ${subscription.id}`);
    }
    const customer = await Customer.findByPk(subscription.customer_id);
    if (!customer) {
      throw new Error(`Customer not found: ${subscription.customer_id}`);
    }

    const { invoice } = this.options;
    const paymentIntent = await PaymentIntent.findByPk(invoice.payment_intent_id);
    const paymentCurrency = (await PaymentCurrency.findOne({
      where: {
        id: invoice.currency_id,
      },
    })) as PaymentCurrency;

    const checkoutSession = await CheckoutSession.findBySubscriptionId(subscription.id);

    const userDid: string = customer.did;
    const locale = await getUserLocale(userDid);
    const productName = await getMainProductName(subscription.id);
    const at: string = formatTime(Date.now());
    const reason: string = await this.getReason(userDid, invoice, locale);

    const hasNft: boolean = checkoutSession?.nft_mint_status === 'minted';
    const nftMintItem: NftMintItem | undefined = hasNft
      ? checkoutSession?.nft_mint_details?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]
      : undefined;

    const currentPeriodStart: string = formatTime(invoice.period_start * 1000);
    const currentPeriodEnd: string = formatTime(invoice.period_end * 1000);
    const duration: string = prettyMsI18n(
      new Date(currentPeriodEnd).getTime() - new Date(currentPeriodStart).getTime(),
      {
        locale: getPrettyMsI18nLocale(locale),
      }
    );
    const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(invoice.default_payment_method_id);

    const paymentInfo: string = formatCurrencyInfo(invoice.amount_remaining, paymentCurrency, paymentMethod);

    const chainHost: string | undefined =
      paymentMethod?.settings?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]?.api_host;
    const viewSubscriptionLink = getCustomerSubscriptionPageUrl({
      subscriptionId: subscription.id,
      locale,
      userDid,
    });
    const viewInvoiceLink = getCustomerInvoicePageUrl({
      invoiceId: invoice.id,
      userDid,
      locale,
      action: 'pay',
    });
    const txHash: string | undefined =
      paymentIntent?.payment_details?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]?.tx_hash;
    const viewTxHashLink: string | undefined =
      hasNft && txHash
        ? getExplorerLink({
            type: 'tx',
            did: txHash,
            chainHost,
          })
        : undefined;
    const customActions = getSubscriptionNotificationCustomActions(
      subscription,
      'customer.subscription.renew_failed',
      locale
    );
    return {
      locale,
      productName,
      at,
      reason,

      nftMintItem,
      userDid,
      paymentInfo,
      currentPeriodStart,
      currentPeriodEnd,
      duration,

      viewSubscriptionLink,
      viewInvoiceLink,
      viewTxHashLink,
      customActions,
      payer:
        paymentMethod?.type !== 'stripe' && getSubscriptionPaymentAddress(subscription, paymentMethod?.type as string),
    };
  }

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

    const template: BaseEmailTemplateType = {
      title: `${translate('notification.subscriptionRenewFailed.title', locale, {
        productName,
      })}`,
      body: `${translate('notification.subscriptionRenewFailed.body', locale, {
        at,
        productName,
      })}`,
      // @ts-expect-error
      attachments: [
        {
          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,
              },
            },
            ...(payer
              ? [
                  {
                    type: 'text',
                    data: {
                      type: 'plain',
                      color: '#9397A1',
                      text: translate('notification.common.payer', locale),
                    },
                  },
                  {
                    type: 'text',
                    data: {
                      type: 'plain',
                      text: payer,
                    },
                  },
                ]
              : []),
            {
              type: 'text',
              data: {
                type: 'plain',
                color: '#9397A1',
                text: translate('notification.common.paymentAmount', locale),
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                text: paymentInfo,
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                color: '#9397A1',
                text: translate('notification.common.failReason', locale),
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                color: '#FF0000',
                text: reason,
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                color: '#9397A1',
                text: translate('notification.common.validityPeriod', locale),
              },
            },
            {
              type: 'text',
              data: {
                type: 'plain',
                text: `${currentPeriodStart} ~ ${currentPeriodEnd}(${duration})`,
              },
            },
            nftMintItem && {
              type: 'text',
              data: {
                type: 'plain',
                color: '#9397A1',
                text: translate('notification.common.nftAddress', locale),
              },
            },
            nftMintItem && {
              type: 'text',
              data: {
                type: 'plain',
                text: toDid(nftMintItem.address),
              },
            },
          ].filter(Boolean),
        },
      ].filter(Boolean),
      // @ts-ignore
      actions: [
        {
          name: translate('notification.common.viewSubscription', locale),
          title: translate('notification.common.viewSubscription', locale),
          link: viewSubscriptionLink,
        },
        {
          name: translate('notification.common.renewNow', locale),
          title: translate('notification.common.renewNow', locale),
          link: viewInvoiceLink,
        },
        viewTxHashLink && {
          name: translate('notification.common.viewTxHash', locale),
          title: translate('notification.common.viewTxHash', locale),
          link: viewTxHashLink as string,
        },
        ...customActions,
      ].filter(Boolean),
    };

    return template;
  }
}
