import Decimal from 'decimal.js';
import mongoose from 'mongoose';

import { MODEL_NAMES } from '../models/_models';
import { EChargeType, ILedgerDataRow } from './reports.db';
import { ILoanProductDoc } from '../models/LoanProducts.model';
import { IProductBrokerDocWithBroker, ProductBrokerModel } from '../models/ProductBroker.model';
import { LoanBrokerModel } from '../models/LoanBroker.model';

export const getProductBrokers = async (productId: string) => {
  return ProductBrokerModel.aggregate<IProductBrokerDocWithBroker>([
    {
      '$match': {
        'productId': new mongoose.Types.ObjectId(productId),
      },
    }, {
      $sort: { 'order': 1 },
    }, {
      $lookup: {
        from: MODEL_NAMES.loanBrokers,
        localField: 'loanBrokerId',
        foreignField: '_id',
        as: 'loanBroker',
      },
    }, {
      $unwind: {
        path: '$loanBroker',
        preserveNullAndEmptyArrays: true,
      },
    }, {
      $unset: ['createdAt', 'updatedAt', '__v'],
    },
  ]);
};

export const enrichWithBrokers = async (transactions: ILedgerDataRow[], product: ILoanProductDoc): Promise<ILedgerDataRow[]> => {
  const productBrokers = await getProductBrokers(product._id.toString());
  if (productBrokers.length === 0) {
    return transactions;
  }
  const handleFee = (t: ILedgerDataRow, broker: IProductBrokerDocWithBroker, shareName: 'adminShare' | 'interestShare' | 'otherShare'): ILedgerDataRow => {
    return {
      ...t,
      statementAmount: new Decimal(-t.statementAmount).mul(broker[shareName]).toDP(2).toNumber(),
      title: `${t.title.replace('ACCRUED', '').trim()}: ${broker.loanBroker.name}`,
      chargeCode: broker.BSCode,
      PLCode: broker.PLCode,
      isBrokerFee: true,
    };
  };

  const handlers = {
    [EChargeType.ADMIN_FEE]: (t: ILedgerDataRow, broker: IProductBrokerDocWithBroker) => handleFee(t, broker, 'adminShare'),
    [EChargeType.INTEREST_FEE]: (t: ILedgerDataRow, broker: IProductBrokerDocWithBroker) => handleFee(t, broker, 'interestShare'),
    [EChargeType.OTHER]: (t: ILedgerDataRow, broker: IProductBrokerDocWithBroker) => handleFee(t, broker, 'otherShare'),
  };

  return transactions.reduce((acc, t) => {
    const handler = handlers[EChargeType[t.type]];
    return handler ? [...acc, t, ...productBrokers.map((broker) => handler(t, broker))] : [...acc, t];
  }, <ILedgerDataRow[]>[]);
};

export const getBorrowerBrokers = async (borrowerId: string) => {
  return ProductBrokerModel
    .find({ borrowerId }, { createdAt: 0, updatedAt: 0, __v: 0 })
    .sort({ order: 1 })
    .lean();
};

export const getAllProductBrokers = async () => {
  return ProductBrokerModel
    .find({}, { createdAt: 0, updatedAt: 0, __v: 0 })
    .sort({ order: 1 })
    .lean();
};

export const getAllLoanBrokers = async () => {
  const results = await LoanBrokerModel
    .find({}, { createdAt: 0, updatedAt: 0, __v: 0 })
    .sort({ order: 1 })
    .lean();
  return results.map((result) => ({
    ...result,
    emails: result.emails ?? [],
  }));
};

export const getBrokerProducts = async (loanBrokerId: string) => {
  return ProductBrokerModel
    .find({ loanBrokerId }, { createdAt: 0, updatedAt: 0, __v: 0 })
    .sort({ order: 1, BSCode: 1, PLCode: 1 })
    .lean();
};
