/* eslint-disable @typescript-eslint/brace-style */
/* eslint-disable @typescript-eslint/indent */
import { withQuery } from 'ufo';
import { BN } from '@ocap/util';
import { getUserLocale } from '../../../integrations/blocklet/notification';
import { translate } from '../../../locales';
import { Customer, PaymentCurrency, CreditGrant } from '../../../store/models';
import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
import { formatCreditAmount, formatTokenAmount, getConnectQueryParam, getCustomerIndexUrl } from '../../util';
import { getRechargePaymentUrl } from '../../currency';

export interface CustomerCreditLowBalanceEmailTemplateOptions {
  customerId: string;
  currencyId: string;
  availableAmount: string; // unit amount
  totalAmount: string; // unit amount
  percentage: string; // 0-100 number string
}

interface CustomerCreditLowBalanceEmailTemplateContext {
  locale: string;
  userDid: string;
  availableAmount: string; // formatted with symbol
  lowBalancePercentage: string; // with % or "less than 1%"
  currencyName: string;
  isCritical: boolean; // true if percentage < 1%
  rechargeUrl: string | null;
}
export class CustomerCreditLowBalanceEmailTemplate
  implements BaseEmailTemplate<CustomerCreditLowBalanceEmailTemplateContext>
{
  // Notification configuration: 10 minute grace period before sending
  static readonly delay = 10 * 60; // seconds

  /**
   * Get low balance threshold percentage from environment variable
   * @returns threshold percentage (default: 10)
   */
  static getLowBalanceThresholdPercent(): number {
    return parseInt(process.env.CREDIT_LOW_BALANCE_THRESHOLD_PERCENTAGE || '10', 10);
  }

  /**
   * Preflight check: verify if notification should still be sent
   * Returns false if user has recharged during grace period
   */
  static async preflightCheck(options: CustomerCreditLowBalanceEmailTemplateOptions): Promise<boolean> {
    try {
      const { customerId, currencyId } = options;

      const availableGrants = await CreditGrant.getAvailableCreditsForCustomer(customerId, currencyId);
      const totalAmount = availableGrants.reduce((sum, grant) => sum.add(new BN(grant.amount)), new BN(0));
      const remainingAmount = availableGrants.reduce(
        (sum, grant) => sum.add(new BN(grant.remaining_amount)),
        new BN(0)
      );

      // Skip if no credits exist
      if (totalAmount.lte(new BN(0))) {
        return false;
      }

      // Calculate current percentage
      const currentPercentage = remainingAmount.mul(new BN(100)).div(totalAmount).toNumber();

      // If balance is above threshold, user has recharged - skip notification
      return currentPercentage <= CustomerCreditLowBalanceEmailTemplate.getLowBalanceThresholdPercent();
    } catch {
      // On error, proceed with sending to avoid missing important notifications
      return true;
    }
  }

  options: CustomerCreditLowBalanceEmailTemplateOptions;

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

  async getContext(): Promise<CustomerCreditLowBalanceEmailTemplateContext> {
    const { customerId, currencyId } = this.options;

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

    const paymentCurrency = await PaymentCurrency.scope('withRechargeConfig').findByPk(currencyId);
    if (!paymentCurrency) {
      throw new Error(`PaymentCurrency not found: ${currencyId}`);
    }

    // Fetch latest balance data instead of using stale data from options
    const availableGrants = await CreditGrant.getAvailableCreditsForCustomer(customerId, currencyId);
    const totalAmount = availableGrants.reduce((sum, grant) => sum.add(new BN(grant.amount)), new BN(0));
    const remainingAmount = availableGrants.reduce((sum, grant) => sum.add(new BN(grant.remaining_amount)), new BN(0));

    const currentPercentage = totalAmount.gt(new BN(0))
      ? remainingAmount.mul(new BN(100)).div(totalAmount).toNumber()
      : 0;

    const userDid = customer.did;
    const locale = await getUserLocale(userDid);

    const formattedAmount = formatTokenAmount(remainingAmount.toString(), paymentCurrency.decimal);
    const available = formatCreditAmount(formattedAmount || '0', paymentCurrency.symbol);
    const isCritical = currentPercentage < 1;
    const lowBalancePercentage = isCritical
      ? translate('notification.creditLowBalance.lessThanOnePercent', locale)
      : `${currentPercentage}%`;

    let rechargeUrl: string | null = await getRechargePaymentUrl(paymentCurrency);
    if (rechargeUrl) {
      rechargeUrl = withQuery(rechargeUrl, { ...getConnectQueryParam({ userDid }) });
    }

    return {
      locale,
      userDid,
      availableAmount: available,
      lowBalancePercentage,
      currencyName: paymentCurrency.name,
      isCritical,
      rechargeUrl,
    };
  }

  async getTemplate(): Promise<BaseEmailTemplateType> {
    const context = await this.getContext();
    const { locale, userDid, availableAmount, lowBalancePercentage, currencyName, isCritical, rechargeUrl } = context;

    const 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.creditLowBalance.remainingBalance', locale),
        },
      },
      {
        type: 'text',
        data: {
          type: 'plain',
          color: isCritical ? '#FF0000' : '#FF6600',
          text: availableAmount,
        },
      },
      {
        type: 'text',
        data: {
          type: 'plain',
          color: '#9397A1',
          text: translate('notification.creditLowBalance.status', locale),
        },
      },
      {
        type: 'text',
        data: {
          type: 'plain',
          color: isCritical ? '#FF0000' : '#FF6600',
          text: lowBalancePercentage,
        },
      },
    ];

    const customerIndexUrl = getCustomerIndexUrl({
      locale,
      userDid,
    });

    const actions = [
      rechargeUrl && {
        name: translate('notification.common.reloadCredits', locale),
        title: translate('notification.common.reloadCredits', locale),
        link: rechargeUrl,
      },
      {
        name: translate('notification.common.manageCredit', locale),
        title: translate('notification.common.manageCredit', locale),
        link: customerIndexUrl,
      },
    ].filter(Boolean);

    const template: BaseEmailTemplateType = {
      title: translate('notification.creditLowBalance.title', locale),
      body: translate('notification.creditLowBalance.body', locale, {
        currency: currencyName,
        availableAmount,
        lowBalancePercentage,
      }),
      attachments: [
        {
          type: 'section',
          fields,
        },
      ],
      // @ts-ignore
      actions,
    };

    return template;
  }
}
