import { BN } from '@ocap/util';
import { translate } from '../../../locales';
import { BaseEmailTemplate, BaseEmailTemplateType } from './base';
import { Subscription, Invoice, Customer, PaymentCurrency, PaymentMethod } from '../../../store/models';
import { getCustomerSubscriptionPageUrl } from '../../subscription';
import { getUserLocale } from '../../../integrations/blocklet/notification';
import { formatTime } from '../../time';
import { formatCurrencyInfo } from '../../util';

export interface AggregatedSubscriptionRenewedEmailTemplateOptions {
  customer_id: string;
  items: Array<{
    event_id: string;
    occurred_at: number;
    data: {
      subscriptionId: string;
      invoiceId: string;
    };
  }>;
  time_range: {
    start: number;
    end: number;
  };
}

interface SubscriptionWithAmount {
  subscription: Subscription;
  amounts: Record<string, string>;
}

interface Context {
  locale: string;
  startTime: string;
  endTime: string;
  totalAmountStr: string;
  subscriptionData: SubscriptionWithAmount[];
  userDid: string;
}

export class AggregatedSubscriptionRenewedEmailTemplate implements BaseEmailTemplate {
  options: AggregatedSubscriptionRenewedEmailTemplateOptions;

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

  async getContext(): Promise<Context> {
    const { items, customer_id: customerId, time_range: timeRange } = this.options;

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

    const subscriptions = await Subscription.findAll({
      where: { id: [...new Set(items.map((item) => item.data.subscriptionId))] },
    });

    const invoices = await Invoice.findAll({
      where: { id: [...new Set(items.map((item) => item.data.invoiceId))] },
    });

    const currencyIds = [...new Set(invoices.map((invoice) => invoice.currency_id))];
    const paymentCurrencies = (await PaymentCurrency.findAll({
      where: { id: currencyIds },
      include: [{ model: PaymentMethod, as: 'payment_method' }],
    })) as (PaymentCurrency & { payment_method: PaymentMethod })[];
    const currencyMap = paymentCurrencies.reduce(
      (acc, curr) => {
        acc[curr.id] = curr;
        return acc;
      },
      {} as Record<string, PaymentCurrency & { payment_method: PaymentMethod }>
    );

    const { totalAmounts, subscriptionAmounts } = invoices.reduce(
      (acc, invoice) => {
        const currency = invoice.currency_id;
        const amount = new BN(invoice.amount_paid || '0');

        acc.totalAmounts[currency] = (acc.totalAmounts[currency] || new BN('0')).add(amount);

        if (invoice.subscription_id) {
          if (!acc.subscriptionAmounts[invoice.subscription_id]) {
            acc.subscriptionAmounts[invoice.subscription_id] = {};
          }
          const subAmounts = acc.subscriptionAmounts[invoice.subscription_id] || {};
          subAmounts[currency] = (subAmounts[currency] || new BN('0')).add(amount);
          acc.subscriptionAmounts[invoice.subscription_id] = subAmounts;
        }
        return acc;
      },
      {
        totalAmounts: {} as Record<string, BN>,
        subscriptionAmounts: {} as Record<string, Record<string, BN>>,
      }
    );

    const totalAmountStr = Object.entries(totalAmounts)
      .map(([currencyId, amount]) => {
        const currency = currencyMap[currencyId];
        if (!currency) {
          return undefined;
        }
        return formatCurrencyInfo(amount.toString(), currency, currency.payment_method);
      })
      .filter((amountStr) => amountStr !== undefined)
      .join('、');

    const subscriptionData = subscriptions.map((subscription) => ({
      subscription,
      amounts: Object.entries(subscriptionAmounts[subscription.id] || {}).reduce(
        (acc, [currencyId, amount]) => {
          const currency = currencyMap[currencyId];
          if (!currency) {
            return acc;
          }
          acc[currencyId] = formatCurrencyInfo(amount.toString(), currency, currency.payment_method);
          return acc;
        },
        {} as Record<string, string>
      ),
    }));

    return {
      locale,
      startTime: formatTime(timeRange.start * 1000),
      endTime: formatTime(timeRange.end * 1000),
      totalAmountStr,
      subscriptionData,
      userDid: customer.did,
    };
  }

  async getTemplate(): Promise<BaseEmailTemplateType> {
    const { locale, startTime, endTime, totalAmountStr, subscriptionData, userDid } = await this.getContext();

    const subscriptionList = subscriptionData
      .map(({ subscription, amounts }) => {
        const amountStr = Object.values(amounts).join('、');
        const description = subscription.description || subscription.id;
        const link = getCustomerSubscriptionPageUrl({
          subscriptionId: subscription.id,
          locale,
          userDid,
        });
        return `<${description} - ${amountStr}(link:${link})>`;
      })
      .join('\n');

    return {
      title: translate('notification.aggregatedSubscriptionRenewed.title', locale, {
        count: subscriptionData.length,
      }),
      body: translate('notification.aggregatedSubscriptionRenewed.body', locale, {
        startTime,
        endTime,
        count: subscriptionData.length,
        totalAmount: totalAmountStr,
        subscriptionList,
      }),
    };
  }
}
