import Decimal from 'decimal.js';
import fs from 'fs';
import path from 'path';
import pdfMake from 'pdfmake/build/pdfmake';
import vfsFonts from 'pdfmake/build/vfs_fonts';
import { TDocumentDefinitions } from 'pdfmake/interfaces';
import dayjs from 'dayjs';

import { LoanProduct } from '../models/LoanProducts.model';
import { BorrowerModel } from '../models/Borrower.model';
import { ELoanChargeType } from '../enums/loan-charge-type.enum';
import { formatNumbers } from '../helpers/numbers.helper';
import { ELoanTypes } from '../enums/loan-types.enum';
import { CUSTOMER_NUMBER_SEPARATOR } from '../db/receivables.db';

import { EFinancialIndex, FinancialIndexesService } from './financial-indexes.service';
import { LoanChargesService } from './loan-charges.service';
import { LoanStatementService } from './loan-statement.service';
import { LoanStatementBalanceService } from './loan-statement-balance.service';
import { LoanTransactionsService } from './loan-transactions.service';
import { OrganizationsService } from './organizations.service';
import { TermLoanService } from './term-loan.service';

interface IPdfServiceConfig {
  rootDir: string;
  title: string;
}

const mappedChargeTypes = {
  [ELoanChargeType.INTEREST_FEE]: 'Interest',
  [ELoanChargeType.ADMIN_FEE]: 'Loan Administration Fee',
  [ELoanChargeType.UNUSED_LINE_FEE]: 'Unused Line Fee',
  [ELoanChargeType.WIRE_FEE]: 'Wire fee',
  [ELoanChargeType.ANNUAL_LINE_FEE]: 'Annual Line Fee',
  [ELoanChargeType.OTHER]: 'Other',
  [ELoanChargeType.RECOVERABLE]: 'Recoverable',
};

export const defaultPDFPageMarginSize = 30;

export const defaultPDFPageSize = {
  width: 841.890, // Page width in points for A4 size (8.27 inches)
  height: 595.276, // Page height in points for A4 size (11.69 inches)
};

export const defaultPDFPageSettings = {
  margin: defaultPDFPageMarginSize,
  docWidth: defaultPDFPageSize.width - defaultPDFPageMarginSize * 2,
  docHeight: defaultPDFPageSize.width - defaultPDFPageMarginSize * 2,
};

const defaultDateFormat = 'MM/DD/YYYY';

export class PdfService {
  private readonly config: IPdfServiceConfig;
  margin = defaultPDFPageMarginSize;
  docWidth: number;
  docHeight: number;

  constructor(
    config: IPdfServiceConfig,
    private readonly financialIndexesService: FinancialIndexesService,
    private readonly loanChargesService: LoanChargesService,
    private readonly loanStatementBalanceService: LoanStatementBalanceService,
    private readonly loanStatementService: LoanStatementService,
    private readonly loanTransactionsService: LoanTransactionsService,
    private readonly organizationsService: OrganizationsService,
    private readonly termLoanService: TermLoanService,
  ) {
    this.config = config;
  }

  public addHeaderNew(organizationSubfolder: string) {

    const imageData = (imageUrl: string) => {
      const imagePath = path.join(this.config.rootDir, imageUrl);
      const imageBuffer = fs.readFileSync(imagePath);
      const imageBase64 = Buffer.from(imageBuffer).toString('base64');
      return `data:image/png;base64,${imageBase64}`;
    };

    const logoElement = {
      image: imageData(`./public/images/${organizationSubfolder}/logo.png`),
      width: 150,
    };

    const gradientElement = {
      image: imageData(`./public/images/${organizationSubfolder}/gradient.png`),
      width: this.docWidth * 0.8,
      height: 60,
    };

    return {
      alignment: 'justify',
      columns: [
        logoElement,
        gradientElement,
      ],
    };
  }

  private async getProductStatementData(productId: string, start: string, end: string) {
    const product = await LoanProduct.findById(productId).lean();
    const borrower = await BorrowerModel.findById(product.borrowerId).lean();
    const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
    const periodStart = dayjs(start);

    const getInterestFee = (adminFeePercent: number) => {
      const fullFee = new Decimal(primeIndex).mul(100).add(adminFeePercent ?? 0).toDP(4).toNumber();
      if (product.type === ELoanTypes.TERM) {
        const minPercent = (product.minPercent ?? 0) === 0 ? 0 : new Decimal(product.minPercent).mul(100).toNumber() ?? 0;
        const maxPercent = (product.maxPercent ?? 0) === 0 ? 100 : new Decimal(product.maxPercent).mul(100).toNumber();
        return Math.min(Math.max(fullFee, minPercent), maxPercent);
      }
      return fullFee;
    };

    const getAdminFeeMin = () => {
      const adminFee = charges.find((charge) => charge.chargeType === ELoanChargeType.ADMIN_FEE);
      if (!adminFee) {
        return '';
      }
      if (!!adminFee.minimumAmount && adminFee.minimumAmount > 0) {
        return `(min = ${adminFee.minimumAmount}$)`;
      }
      return '';
    };

    const {
      groupedData,
      otherStatements,
      transactions,
    } = await this.loanStatementService.getProductStatementPdfData(productId, start, end);

    const chargeInfo = (chargeType: ELoanChargeType) => {
      const foundCharge = charges.find((charge) => charge.chargeType === chargeType);
      if (!foundCharge) {
        return { percent: null, minimumAmount: null };
      }
      return { percent: foundCharge.percent * 100, minimumAmount: foundCharge.minimumAmount };
    };
    const primeIndex = await this.financialIndexesService.getFinancialIndexValue(EFinancialIndex.PRIME_RATE) ?? 0;

    const chargeMap: { [charge: string]: { title: string, explanation: string } } = {
      [ELoanChargeType.INTEREST_FEE]: {
        title: mappedChargeTypes[ELoanChargeType.INTEREST_FEE],
        explanation: `${getInterestFee(chargeInfo(ELoanChargeType.INTEREST_FEE).percent)}% - daily charge`,
      },
      [ELoanChargeType.ADMIN_FEE]: {
        title: mappedChargeTypes[ELoanChargeType.ADMIN_FEE],
        explanation: `${chargeInfo(ELoanChargeType.ADMIN_FEE).percent}% - per month, average monthly balance ${getAdminFeeMin()}`,
      },
      [ELoanChargeType.UNUSED_LINE_FEE]: {
        title: mappedChargeTypes[ELoanChargeType.UNUSED_LINE_FEE],
        explanation: `${chargeInfo(ELoanChargeType.UNUSED_LINE_FEE).percent}% - daily charge on unused amount`,
      },
      [ELoanChargeType.WIRE_FEE]: {
        title: mappedChargeTypes[ELoanChargeType.WIRE_FEE],
        explanation: `${chargeInfo(ELoanChargeType.WIRE_FEE).minimumAmount}$ per disbursement`,
      },
      [ELoanChargeType.ANNUAL_LINE_FEE]: {
        title: mappedChargeTypes[ELoanChargeType.ANNUAL_LINE_FEE],
        explanation: `${chargeInfo(ELoanChargeType.ANNUAL_LINE_FEE).percent}% of commitment`,
      },
      [ELoanChargeType.OTHER]: {
        title: mappedChargeTypes[ELoanChargeType.OTHER],
        explanation: '',
      },
      [ELoanChargeType.RECOVERABLE]: {
        title: mappedChargeTypes[ELoanChargeType.RECOVERABLE],
        explanation: '',
      },
    };

    const preparedStatement = Object.entries(groupedData).map(([chargeType, amount]) => {
      if (amount === 0) {
        return null;
      }
      return [
        chargeMap[chargeType].title,
        { text: formatNumbers(amount), alignment: 'right' },
        { text: chargeMap[chargeType].explanation, noWrap: true },
      ];
    });

    const groupedOtherStatements: {
      [chargeName: string]: { memo: string, amount: number, chargeName: string }
    } = otherStatements.reduce((acc, otherStatement) => {
      const groupName = `${otherStatement.chargeName}${CUSTOMER_NUMBER_SEPARATOR}${otherStatement.memo}`;
      const foundValue = acc[groupName];
      if (foundValue) {
        acc[groupName].amount = new Decimal(foundValue.amount).add(otherStatement.amount).toNumber();
        return acc;
      }
      return {
        ...acc,
        [groupName]: {
          amount: otherStatement.amount,
          chargeName: otherStatement.chargeName,
          memo: otherStatement.memo,
        },
      };
    }, <{ [chargeName: string]: { memo: string, amount: number, chargeName: string } }>{});

    Object
      .values(groupedOtherStatements)
      .sort((values1, values2) => values1.memo.localeCompare(values2.memo))
      .sort((values1, values2) => values1.chargeName.localeCompare(values2.chargeName))
      .forEach((values) => {
        preparedStatement.push([
          { text: values.chargeName, noWrap: false },
          { text: formatNumbers(values.amount), alignment: 'right' },
          { text: values.memo, noWrap: false },
        ]);
      });

    const previousStatementTransaction = await this.loanStatementBalanceService.getLastStatementTransactionForDate(productId, new Date(periodStart.format('YYYY-MM-DD')));
    const startStatementBalance = previousStatementTransaction ? previousStatementTransaction.balance : 0;

    let monthlyTermPrincipal = null;
    if (product.type === ELoanTypes.TERM) {
      const { monthlyPrincipal } = await this.getTermLoan(product._id.toString(), new Date(start));
      monthlyTermPrincipal = monthlyPrincipal;
    }

    const totalOther = Object.values(groupedOtherStatements).reduce((acc, st) => new Decimal(acc).add(st.amount).toNumber(), 0);
    const other = { other: totalOther };
    const totalAmountDue = Object.values({ ...groupedData, ...other }).reduce((acc, value) => new Decimal(acc).add(value || 0).toNumber(), 0);

    const fullStatementTable = [
      [{ text: '' }, { text: 'AMOUNT', bold: true, alignment: 'right' }, { text: 'BASIS FOR CHARGE', bold: true }],
      ...preparedStatement.filter((statement) => !!statement),
      monthlyTermPrincipal ? [{ text: 'Principal' }, {
        text: formatNumbers(monthlyTermPrincipal),
        alignment: 'right',
      }, { text: '' }] : [],
      [
        { text: 'Past due amount from previous statement', bold: true, border: [false], noWrap: true },
        {
          text: formatNumbers(startStatementBalance),
          bold: true,
          border: [false],
          alignment: 'right',
        },
        { text: '', border: [false] },
      ],
      [{
        text: 'TOTAL AMOUNT DUE',
        bold: true,
        color: 'red',
        border: [false],
        noWrap: true,
      }, {
        text: formatNumbers(new Decimal(totalAmountDue).add(startStatementBalance).add(monthlyTermPrincipal ? monthlyTermPrincipal : 0).toNumber()),
        bold: true,
        color: 'red',
        border: [false],
        alignment: 'right',
      }, { text: '', border: [false] }],
    ].filter((row) => row.length > 0);

    const productInfoTable = [
      [{ text: '' }, { text: '' }],
      [{ text: 'Product start date' }, {
        text: dayjs(product.startDate).format(defaultDateFormat),
        alignment: 'right',
      }],
      [{ text: 'Product maturity date' }, {
        text: dayjs(product.maturityDate).format(defaultDateFormat),
        alignment: 'right',
      }],
      [{ text: 'Commitment amount' }, { text: formatNumbers(product.commitment), alignment: 'right' }],
    ];

    const endDate = product.deactivationDate
      ? dayjs(product.deactivationDate).isBefore(dayjs(end.slice(0, 10))) ? dayjs(product.deactivationDate) : dayjs(end.slice(0, 10))
      : dayjs(end.slice(0, 10));

    const preparedStatementHeader = [
      { text: `STATEMENT FOR PRODUCT: ${product.name}`, bold: true },
      { text: `Client: ${borrower.code}`, bold: true },
      {
        text: `PERIOD FROM ${dayjs.utc(start).format(defaultDateFormat)} TO ${endDate.format(defaultDateFormat)}`,
        bold: true,
      },
      {
        table: { widths: [120, 140], body: productInfoTable },
        layout: 'lightHorizontalLines',
      },
    ];

    const previousTransaction = transactions.length > 0
      ? await this.loanTransactionsService.getLastTransactionForDate(transactions[0].productId.toString(), dayjs(transactions[0].date).subtract(1, 'second').toDate())
      : await this.loanTransactionsService.getLastTransactionForDate(productId, dayjs(periodStart).subtract(1, 'second').toDate());

    const lastTransaction = transactions.length > 0
      ? transactions[transactions.length - 1]
      : previousTransaction;

    const preparedTransactionsHeader = [
      { text: `LOAN TRANSACTIONS: ${product.name}` },
    ];

    const preparedTransactions = transactions.map((transaction) => {
      return [
        { text: dayjs(transaction.date).format(defaultDateFormat) },
        { text: transaction.transactionType },
        { text: formatNumbers(transaction.amount), alignment: 'right' },
        { text: transaction.reference },
        { text: formatNumbers(transaction.balance), alignment: 'right' },
      ];
    });

    const fullTransactionsTable = [
      [
        { text: 'Date' },
        { text: 'Type' },
        { text: 'Amount', alignment: 'right' },
        { text: 'Reference' },
        { text: 'Balance', alignment: 'right' },
      ],
      [
        { text: '' },
        { text: '' },
        { text: '' },
        { text: 'OPENING BALANCE' },
        { text: `${previousTransaction ? formatNumbers(previousTransaction.balance) : 0}`, alignment: 'right' },
      ],
      ...preparedTransactions,
      [
        { text: '' },
        { text: '' },
        { text: '' },
        { text: 'CLOSING BALANCE' },
        { text: `${lastTransaction ? formatNumbers(lastTransaction.balance) : 0}`, alignment: 'right' },
      ],
    ];

    return {
      startStatementBalance,
      preparedStatementHeader,
      preparedStatement: {
        table: {
          widths: ['auto', 100, '*'],
          body: fullStatementTable,
        },
        layout: 'lightHorizontalLines',
      },
      preparedTransactionsHeader,
      preparedTransactions: {
        table: {
          widths: [80, 100, 80, '*', 80],
          body: fullTransactionsTable,
        },
        layout: 'lightHorizontalLines',
      },
      groupedData,
      otherStatements,
    };
  }

  async getTermLoan(productId: string, start: Date) {
    const termLoan = await this.termLoanService.getTermLoan(productId, true);
    let isMatch = false;

    const tableHeader = [
      { text: 'Relevant statement', bold: true },
      { text: 'Due date', bold: true },
      { text: 'Interest rate', bold: true, alignment: 'right' },
      { text: 'Closing Balance', bold: true, alignment: 'right' },
      { text: 'Principal', bold: true, alignment: 'right' },
      { text: 'Interest', bold: true, alignment: 'right' },
      { text: 'EMI', bold: true, alignment: 'right' },
      { text: 'Admin Fee', bold: true, alignment: 'right' },
      { text: 'Total Payment', bold: true, alignment: 'right' },
    ];

    let tableData = [];

    let monthlyPrincipal: number = null;
    let dueDate: Date = null;

    const cellParams = { border: [false], noWrap: true, fontSize: 12 };
    const numberCellParams = { ...cellParams, alignment: 'right' };

    if (termLoan) {
      tableData = termLoan.calculated.reduce((acc, calculatedRow) => {
        isMatch = isMatch || dayjs(calculatedRow.relevantStatement).endOf('month').isAfter(dayjs(start));
        if (!isMatch) {
          return acc;
        }
        const EMI = new Decimal(calculatedRow.monthlyPrincipal).add(calculatedRow.monthlyInterest).toNumber();
        const totalPayment = new Decimal(EMI).add(calculatedRow.adminFee).toNumber();
        if (monthlyPrincipal === null) {
          monthlyPrincipal = calculatedRow.monthlyPrincipal;
          dueDate = calculatedRow.paymentDueDate;
        }
        return [
          ...acc,
          [
            { text: dayjs(calculatedRow.relevantStatement).format(defaultDateFormat), ...cellParams },
            { text: dayjs(calculatedRow.paymentDueDate).format(defaultDateFormat), ...cellParams },
            { text: formatNumbers(new Decimal(calculatedRow.interestRate).mul(100).toDP(2).toNumber() + '%'), ...numberCellParams },
            { text: formatNumbers(calculatedRow.closingBalance), ...numberCellParams },
            { text: formatNumbers(calculatedRow.monthlyPrincipal), ...numberCellParams },
            { text: formatNumbers(calculatedRow.monthlyInterest), ...numberCellParams },
            { text: formatNumbers(EMI), ...numberCellParams },
            { text: formatNumbers(calculatedRow.adminFee), ...numberCellParams },
            { text: formatNumbers(totalPayment), ...numberCellParams },
          ],
        ];
      }, []);
    }
    return {
      monthlyPrincipal,
      dueDate,
      termLoanTableData: [tableHeader, ...tableData],
    };
  }

  async createStatementDoc<T extends pdfMake.TCreatedPdf | {
    total: number;
    dueDate: Date,
    productTotals: { [productId: string]: number }
  }>(params: {
    borrowerId: string,
    start: string,
    end: string
  }, getOnlyTotal = false): Promise<T> {

    const { borrowerId, start, end } = params;
    const products = await this.loanChargesService.getLoanProducts(borrowerId);
    const productForPeriod = products.filter(product => {
      if (!product.deactivationDate) {
        return true;
      }
      const deactivation = dayjs(product.deactivationDate);
      return deactivation.isAfter(dayjs(start)) && deactivation.isBefore(dayjs(end));
    });

    const borrower = await BorrowerModel.findById(borrowerId).lean();

    const organization = await this.organizationsService.getOrganizationForBorrower(borrowerId);

    pdfMake.vfs = (vfsFonts as any).pdfMake.vfs;

    const pageSize = {
      width: 841.890, // Page width in points for A4 size (8.27 inches)
      height: 595.276, // Page height in points for A4 size (11.69 inches)
    };

    this.docWidth = defaultPDFPageSize.width - this.margin * 2;
    this.docHeight = defaultPDFPageSize.height - this.margin * 2;

    const emptyString = { text: ' ', margin: 5 };
    const pageBreaker = { text: '', pageBreak: 'after' };

    const totalGroupedData = {
      [ELoanChargeType.INTEREST_FEE]: 0,
      [ELoanChargeType.ADMIN_FEE]: 0,
      [ELoanChargeType.UNUSED_LINE_FEE]: 0,
      [ELoanChargeType.WIRE_FEE]: 0,
      [ELoanChargeType.ANNUAL_LINE_FEE]: 0,
      [ELoanChargeType.OTHER]: 0,
      [ELoanChargeType.RECOVERABLE]: 0,
    };

    let totalOverDueStatementBalance = 0;

    const productTotals: { [productId: string]: number } = productForPeriod.reduce((acc, p) => ({
      ...acc,
      [p._id.toString()]: 0,
    }), {});

    let totalStatementValue = 0;
    const principalData = [];
    let paymentDueDate: Date = null;

    const docContent = await Promise.all(productForPeriod.map(async (product, index) => {
      const {
        startStatementBalance,
        preparedStatementHeader,
        preparedStatement,
        preparedTransactionsHeader,
        preparedTransactions,
        groupedData,
        otherStatements,
      } = await this.getProductStatementData(product._id.toString(), start, end);

      totalOverDueStatementBalance = new Decimal(totalOverDueStatementBalance).add(startStatementBalance).toNumber();

      let totalPerProduct = 0;
      Object.entries(groupedData).forEach(([chargeType, amount]) => {
        totalGroupedData[chargeType] = new Decimal(totalGroupedData[chargeType]).add(amount).toNumber();
        totalPerProduct = new Decimal(totalPerProduct).add(amount).toNumber();
      });
      otherStatements.forEach((otherStatement) => {
        totalGroupedData[otherStatement.chargeType] = new Decimal(totalGroupedData[otherStatement.chargeType]).add(otherStatement.amount).toNumber();
        totalPerProduct = new Decimal(totalPerProduct).add(otherStatement.amount).toNumber();
      });
      productTotals[product._id.toString()] = new Decimal(startStatementBalance).add(totalPerProduct).toNumber();

      const warningText = product.type === ELoanTypes.REVOLVER
        ? `AMOUNT WILL BE ADDED TO THE LOAN ON 1ST. ${organization.name.toUpperCase()} RESERVES THE RIGHT TO STOP FUNDING IF THERE IS INSUFFICIENT AVAILABILITY TO ADD IT TO THE LOAN.`
        : `PAYABLE BY 7TH. ${organization.name.toUpperCase()} RESERVES THE RIGHT TO STOP FUNDING IF THE STATEMENT IS NOT PAID.`;

      const baseContent = [
        this.addHeaderNew(organization.subfolder),
        emptyString,
        ...preparedStatementHeader,
        emptyString,
        preparedStatement,
        emptyString,
        {
          text: warningText,
          color: 'red',
        },
        {
          text: `PAYABLE BY DIRECT TRANSFER, OR BORROWING BASE SUBMISSION CONFIRMING AVAILABILITY. ${organization.name.toUpperCase()} RESERVES THE RIGHT TO ACH FROM YOUR ACCOUNT`,
          bold: true,
        },
        emptyString,
        ...preparedTransactionsHeader,
        preparedTransactions,
      ];

      const productPageEnd = productForPeriod.length === index + 1
        ? emptyString
        : pageBreaker;

      if (product.type === ELoanTypes.TERM) {
        const {
          termLoanTableData,
          monthlyPrincipal,
          dueDate,
        } = await this.getTermLoan(product._id.toString(), new Date(start));

        if (monthlyPrincipal !== null) {
          totalStatementValue = new Decimal(totalStatementValue).add(monthlyPrincipal).toNumber();
          principalData.push([
            { text: `Principal (${product.name})` },
            { text: formatNumbers(monthlyPrincipal), alignment: 'right' },
            { text: '' },
          ]);
          paymentDueDate = dueDate;
          productTotals[product._id.toString()] = new Decimal(productTotals[product._id.toString()]).add(monthlyPrincipal).toNumber();
        }

        return [
          ...baseContent,
          pageBreaker,
          this.addHeaderNew(organization.subfolder),
          emptyString,
          <any>{ text: `Term loan schedule: `, fontSize: 15, bold: true },
          emptyString,
          {
            table: { widths: ['*', '*', '*', '*', '*', '*', '*', '*', '*'], body: termLoanTableData },
            layout: 'lightHorizontalLines',
          },
          productPageEnd,
        ];
      }

      return [
        ...baseContent,
        productPageEnd,
      ];
    }));

    const preparedTotal = Object.entries(totalGroupedData).map(([chargeType, amount]) => {
      totalStatementValue = new Decimal(totalStatementValue).add(amount).toNumber();
      return [
        { text: mappedChargeTypes[ELoanChargeType[chargeType]] },
        { text: formatNumbers(amount), alignment: 'right' },
        { text: '' },
      ];
    });

    const totalOverDueStatement = totalOverDueStatementBalance > 0
      ? [
        { text: 'Past due amount from previous statement' },
        { text: formatNumbers(totalOverDueStatementBalance), alignment: 'right' },
        { text: '' }]
      : [];

    const totalOwed = new Decimal(totalStatementValue).add(totalOverDueStatementBalance).toNumber();

    if (getOnlyTotal) {
      return <T>{ total: totalOwed, dueDate: paymentDueDate, productTotals };
    }

    const bankAccounts = {
      'Black Feather': [
        [{ text: 'Bank name' }, { text: 'BMO N.A' }],
        [{ text: 'ABA' }, { text: '071000288' }],
        [{ text: 'Account' }, { text: '1841261' }],
      ],
      'GemCap': [
        [{ text: 'Bank name' }, { text: 'CIBC Bank USA' }],
        [{ text: 'ABA' }, { text: '0710-0648-6' }],
        [{ text: 'SWIFT' }, { text: 'PVTBUS44' }],
        [{ text: 'Account' }, { text: '2583852' }],
      ],
    };

    const paymentInstruction = [
      [{ text: 'Please remit payment to', fillColor: '#44464b', color: 'white' }, { text: '', fillColor: '#44464b' }],
      ...bankAccounts[organization.name],
      [{ text: 'Account name' }, { text: `${this.config.title} Funding AssetCo LLC Collection Account` }],
      [{ text: 'Reference' }, { text: `${borrower.code} - STATEMENT PAYMENT` }],
      [{ text: 'The reference is required to ensure timely settlement of your account.', color: 'red' }, {}],
    ];

    const totalStatementTable = [
      [{ text: '' }, { text: 'AMOUNT', bold: true, alignment: 'right' }, { text: '' }],
      ...preparedTotal,
      ...principalData,
      totalOverDueStatement,
      [
        { text: 'TOTAL', color: 'red' },
        {
          text: formatNumbers(totalOwed),
          bold: true,
          color: 'red',
          alignment: 'right',
        },
        { text: paymentDueDate ? `due on ${dayjs(paymentDueDate).format(defaultDateFormat)}` : '', color: 'red' },
      ],
    ];

    docContent.unshift([
      this.addHeaderNew(organization.subfolder),
      emptyString,
      <any>{ text: 'Total Statement Amount', fontSize: 18, bold: true },
      { text: `Client: ${borrower.name}`, fontSize: 15, bold: true },
      {
        table: { widths: [300, 100, '*'], body: totalStatementTable.filter((row) => row.length !== 0) },
        layout: 'lightHorizontalLines',
      },
      emptyString,
      {
        table: { widths: ['*', '*'], body: paymentInstruction, styles: 'section' },
        layout: 'lightHorizontalLines',
      },
      pageBreaker,
    ]);

    const docDefinition: TDocumentDefinitions = {
      content: docContent as any,
      footer: (currentPage, pageCount) => {
        return {
          text: 'Page ' + currentPage.toString() + ' of ' + pageCount,
          alignment: 'center',
        };
      },
      pageOrientation: 'landscape',
      pageSize,
      pageMargins: this.margin,
    };

    return <T>pdfMake.createPdf(docDefinition);
  }

}
