import * as fs from 'fs';
import path from 'path';
import dayjs from 'dayjs';
import nodemailer from 'nodemailer';
import { Buffer } from 'buffer';

import { IMulterFile } from '../interfaces/multer.interface';
import { IEmail } from '../interfaces/email.interface';
import { ELoanTypes } from '../enums/loan-types.enum';
import { Organization } from '../models/Organization.model';
import { IComplianceBorrowerDocumentFull } from '../models/BorrowerCompliance.model';

import { IFinancialComplianceBorrowerDocumentWithMails } from '../models/FinancialComplianceBorrower.model';
import { BorrowerWithFiles, FileManagerService } from './file-manager.service';
import FinancialCompliance, { IFinancialComplianceSettingsDocument } from '../models/FinancialCompliance.model';
import { OrganizationEmailsType } from '../models/OrganizationEmails.model';
import { BorrowerService } from './borrowers.service';
import { ComplianceBorrowersService } from './compliance-borrowers.service';
import { FinancialComplianceService } from './financial-compliance.service';
import { LoanChargesService } from './loan-charges.service';
import { OrganizationEmailsService } from './organization-emails.service';
import { OrganizationsService } from './organizations.service';
import { SentryService } from './sentry.service';
import { isProductActive } from '../helpers/date.helper';

export enum ESenderType {
  REGULAR,
  FINANCIAL,
  FINANCIAL_ONLY,
  GOAT,
  NEW_BUSINESS
}

type EmailWithItems = IEmail & {
  recipientName: string,
  hasPastDueItems: boolean,
  complianceBorrowerId: string;
  items: { itemName: string, dateDue: Date, status: string }[];
}

export type EmailWithFinancial = IEmail & {
  borrowers: IFinancialComplianceBorrowerDocumentWithMails[];
}

const getSenderSignatureFileName = (senderType: ESenderType) => {
  switch (senderType) {
    case ESenderType.REGULAR:
      return 'signature.html';
    case ESenderType.FINANCIAL:
      return 'signature-financial.html';
    case ESenderType.FINANCIAL_ONLY:
      return 'signature-financial-only.html';
    case ESenderType.NEW_BUSINESS:
      return 'signature-new-business.html';
    case ESenderType.GOAT:
      return 'signature-goat.html';
    default:
      return 'signature.html';
  }
};

interface INodemailerServiceConfig {
  appTitle: string;
  sender: string;
  senderFinancial: string;
  senderNewBusiness: string;
  hiddenRecipient: string[];
  mailerConfig: {
    host: string,
    port: number,
    auth: {
      user: string,
      pass: string,
    },
  },
}

export class NodemailerService {

  private readonly config: INodemailerServiceConfig;
  private senders: { [key in ESenderType]?: { senderName: string, senderEmail: string } };
  private transporter: nodemailer.Transporter;

  constructor(
    config: INodemailerServiceConfig,
    private readonly borrowerService: BorrowerService,
    private readonly getComplianceBorrowersService: () => ComplianceBorrowersService,
    private readonly fileManagerService: FileManagerService,
    private readonly financialComplianceService: FinancialComplianceService,
    private readonly loanChargesService: LoanChargesService,
    private readonly organizationEmailsService: OrganizationEmailsService,
    private readonly organizationsService: OrganizationsService,
    private readonly sentryService: SentryService,
  ) {
    this.config = config;
    this.senders = {
      [ESenderType.REGULAR]: {
        senderName: this.config.appTitle,
        senderEmail: this.config.sender,
      },
      [ESenderType.FINANCIAL]: {
        senderName: `${this.config.appTitle} Finance Department`,
        senderEmail: this.config.senderFinancial,
      },
      [ESenderType.NEW_BUSINESS]: {
        senderName: `${this.config.appTitle} New Business`,
        senderEmail: this.config.senderNewBusiness,
      },
    };
    this.transporter = nodemailer.createTransport(this.config.mailerConfig);
  }

  private getSender(senderType: ESenderType, senderExtraName?: string) {
    switch (senderType) {
      case ESenderType.REGULAR:
        return `${this.senders[senderType].senderName} <${this.senders[senderType].senderEmail}>`;
      case ESenderType.FINANCIAL:
        return `${this.senders[senderType].senderName} <${this.senders[senderType].senderEmail}>`;
      case ESenderType.NEW_BUSINESS:
        return `"${this.senders[senderType].senderName}${senderExtraName ?? ''}" <${this.senders[senderType].senderEmail}>`;
      default:
        return `"${this.config.appTitle}>" <${this.config.sender}>`;
    }
  };

  private async senderHandler(err, info) {
    if (err) {
      this.sentryService.catchErrorBySentry(null, err);
      throw err;
    }
    console.debug({ info });
    console.log(info.envelope);
    console.log(info.messageId);
    console.log('Message sent: %s', info.messageId);
    console.log('Preview URL: %s', nodemailer.getTestMessageUrl(info));
  }

  async getSignatureAsync(senderType: ESenderType, organizationSubfolder?: string) {
    const signaturePath = path.resolve(__dirname, '../public/emails', organizationSubfolder ? organizationSubfolder : '', getSenderSignatureFileName(senderType));
    return await fs.promises.readFile(signaturePath, 'utf8');
  }

  async collectItemsAndNotify(borrowerId: string) {
    const complianceBorrowersService = this.getComplianceBorrowersService();
    const borrower: IComplianceBorrowerDocumentFull = await complianceBorrowersService.getFullComplianceBorrowerById(borrowerId);
    if (!borrower.isEmailingActive) {
      return;
    }
    let hasPastDueItems = false;
    let allInReview = true;
    const fullItems = borrower.items.reduce((accI, item) => {
      return [...accI, ...item.instances.reduce((accIns, instance) => {
        if (instance.status === 'ACCEPTED') {
          return accIns;
        }

        const currentDate = dayjs();
        const dueDate = dayjs(instance.nextDate);
        const dateToNotify = dayjs(instance.nextDate).subtract(item.item.notificationDays, 'day');

        const diff = currentDate.diff(dueDate);
        const addresses = [
          ...borrower.mainEmails.reduce((acc, address) => address.isActive === true ? [...acc, address.email] : acc, []),
          ...item.emailAddresses.reduce((acc, address) => address.isActive === true ? [...acc, address.email] : acc, [])];
        if (diff > 0) {
          hasPastDueItems = instance.status !== 'IN_REVIEW';
          allInReview = allInReview && instance.status === 'IN_REVIEW';
          return [...accIns, {
            itemName: item.item.name,
            dateDue: instance.nextDate,
            status: instance.status === 'IN_REVIEW' ? 'In review' : 'Past due',
            addresses,
          }];
        }

        const notificationDiff = currentDate.diff(dateToNotify);
        if (notificationDiff > 0 && instance.status !== 'IN_REVIEW') {
          allInReview = allInReview && false;
          return [...accIns, {
            itemName: item.item.name,
            dateDue: instance.nextDate,
            status: 'Waiting for submission',
            addresses,
          }];
        }

        return accIns;
      }, [])];
    }, []);
    if (!allInReview && fullItems.length) {
      const uniqEmails = new Set<string>();
      fullItems.forEach((item) => item.addresses.forEach((address) => uniqEmails.add(address)));

      await Promise.all(Array.from(uniqEmails).map(async (emailAddress) => {
        const email: EmailWithItems = {
          recipientName: borrower.borrower.name,
          complianceBorrowerId: String(borrower._id),
          addresses: [emailAddress],
          subject: 'Reminder',
          text: '',
          hasPastDueItems,
          items: fullItems.filter((item) => item.addresses.includes(emailAddress)),
        };
        await this.sendEmailNotification(email, borrower.borrower._id.toString());
      }));
    }
  }

  async sendEmailNotification(email: EmailWithItems, borrowerId: string) {
    const organizations = await this.organizationsService.getOrganizationForBorrower(borrowerId);
    const senderEmail = await this.organizationEmailsService.getEmail(organizations._id.toString(), OrganizationEmailsType.SENDER);
    const hiddenRecipientEmail = await this.organizationEmailsService.getEmail(organizations._id.toString(), OrganizationEmailsType.HIDDEN_RECIPIENT);
    fs.readFile(
      path.resolve(__dirname, '../public/emails', organizations.subfolder, 'notification.html'),
      'utf8',
      async (error, data) => {
        if (error) {
          console.error({ error });
          return;
        }

        const signature = await this.getSignatureAsync(ESenderType.REGULAR, organizations.subfolder);

        const tableRows = email.items.reduce((acc, item) => {
          return `${acc}
          <tr>
            <td style="border: 1px solid black">${item.itemName}</td>
            <td style="border: 1px solid black">${item.dateDue.toLocaleDateString()}</td>
            <td style="border: 1px solid black">${item.status}</td>
          </tr>`;
        }, '');

        const tableHtml = `
      <table style="width:100%; border: 1px solid black; border-spacing: 0" >
        <tr>
          <th style="border: 2px solid black">Compliance Item</th>
          <th style="border: 2px solid black">Date Due</th>
          <th style="border: 2px solid black">Status</th>
        </tr>
        ${tableRows}
      </table>`;

        const hasPastDueItemsWarning = email.hasPastDueItems
          ? `<p><span style="color: #ff0000;"><strong>You have past due items which is a breach of the obligations under the loan agreement. Failure to submit the above documents may cause a delay in funding and an event of default. If you cannot submit the documents please contact us urgently.</strong></span></p>`
          : '';

        const replacedData = data
          .replace('${recipientName}', email.recipientName)
          .replace('${items}', tableHtml)
          .replace('${hasPastDueItems}', hasPastDueItemsWarning)
          .replace('${signature}', signature);

        try {
          this.transporter.sendMail({
            from: `${senderEmail.title} <${senderEmail.address}>`,
            to: email.addresses,
            bcc: `${hiddenRecipientEmail.title} <${hiddenRecipientEmail.address}>`,
            subject: email.subject,
            text: 'non-html',
            html: replacedData,
          }, async (err, info) => {
            await this.senderHandler(err, info);
            const complianceBorrowersService = this.getComplianceBorrowersService();
            await complianceBorrowersService.updateLastEmailSent(email.complianceBorrowerId, new Date());
          });
        } catch (e) {
          this.sentryService.catchErrorBySentry(null, e);
        }
      });
  }

  async sendDailyUpdates() {
    fs.readFile(
      path.resolve(__dirname, '../public/emails', 'daily-updates.html'),
      'utf8',
      async (error, data) => {
        if (error) {
          console.error({ error });
          return;
        }

        const signature = await this.getSignatureAsync(ESenderType.REGULAR);

        const borrowerWithFiles: BorrowerWithFiles[] = await this.fileManagerService.getFilesNewerThanDate(dayjs().startOf('day').toDate());

        if (!borrowerWithFiles.length) {
          return;
        }

        const borrowerTables = borrowerWithFiles.reduce((allBorrowers, borrower) => {

          const tableRows = borrower.files.reduce((borrowerAcc, file) => {
            return `
              ${borrowerAcc}
              <tr>
                <td style="border: 1px solid black">${file.name}</td>
                <td style="border: 1px solid black">${file.uploadedAt.toLocaleDateString()}</td>
              </tr>`;
          }, '');

          const borrowerLink = `https://analytics.traxabl.com/compliance/borrowers/${borrower._id}`;

          return `
            ${allBorrowers}
            <br>
            <a href="${borrowerLink}">${borrower.borrower.name}</a>
            <table style="width:100%; border: 1px solid black; border-spacing: 0">
              <tr>
                <th style="border: 2px solid black">File name</th>
                <th style="border: 2px solid black">Uploaded at</th>
              </tr>
            ${tableRows}
            </table>`;
        }, '');

        const financialSettings: IFinancialComplianceSettingsDocument = await FinancialCompliance.findOne({});
        const financialEmails = financialSettings.financialEmails
          .reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);

        const replacedData = data
          .replace('${borrowerTables}', borrowerTables)
          .replace('${signature}', signature);

        try {
          this.transporter.sendMail({
            from: this.getSender(ESenderType.REGULAR),
            to: financialEmails,
            bcc: [],
            subject: `${this.config.appTitle} / Daily uploads`,
            text: 'non-html',
            html: replacedData,
          }, async (err, info) => {
            await this.senderHandler(err, info);
          });
        } catch (e) {
          this.sentryService.catchErrorBySentry(null, e);
        }
      });
  }

  async sendSimpleNotificationMail(email: IEmail) {
    this.transporter.sendMail({
      from: this.getSender(ESenderType.REGULAR),
      to: email.addresses,
      bcc: this.config.hiddenRecipient,
      subject: email.subject,
      text: email.text,
      html: `<b>${email.text}</b>`,
    }, async (err, info) => {
      await this.senderHandler(err, info);
    });
  }

  async sendFinancialExternal(email: IEmail, organizationId: string, stopFunding = false) {
    const organization = await Organization.findById(organizationId).lean();
    const senderEmail = await this.organizationEmailsService.getEmail(organizationId, OrganizationEmailsType.SENDER);
    const financialEmail = await this.organizationEmailsService.getEmail(organizationId, OrganizationEmailsType.FINANCIAL);
    fs.readFile(
      path.resolve(__dirname, '../public/emails', organization.subfolder, stopFunding ? 'financial-external-on-stop.html' : 'financial-external-until-stop_date.html'),
      'utf8',
      async (error, data) => {
        if (error) {
          console.error({ error });
          return;
        }

        const financialSettings = await this.financialComplianceService.getFinancialComplianceSettings();
        const financialEmails = financialSettings.financialEmails
          .reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);

        const signature = await this.getSignatureAsync(ESenderType.FINANCIAL, organization.subfolder);

        const borrower: IFinancialComplianceBorrowerDocumentWithMails = email.customData;

        const replyText = `I confirm that we have sufficient availability and request to add the statement balance of ${new Intl.NumberFormat('us-US').format(borrower.balanceOutstanding)} to the loan.`;

        const mailTo: string[] = [senderEmail.address, financialEmail.address];

        const replyLink = `<a href="mailto:${mailTo.join(',')}?subject=Re:%20Email%20Answer&body=${replyText.replace(' ', '%20')}">by reply to this email</a>`;

        const replacedData = data
          .replaceAll('${BORROWER_NAME}', borrower.borrower.title)
          .replaceAll('${REMINDER_COUNTER}', (borrower.reminderCounter + 1).toString())
          .replaceAll('${ORIGINAL_BALANCE}', new Intl.NumberFormat('us-US').format(borrower.amountOwed))
          .replaceAll('${AMOUNT_OUTSTANDING}', new Intl.NumberFormat('us-US').format(borrower.balanceOutstanding))
          .replaceAll('${DUE_DATE}', dayjs(borrower.dueDate).format('M/D/YYYY'))
          .replaceAll('${REPLY_LINK}', replyLink)
          .replaceAll('${signature}', signature);

        return this.transporter.sendMail({
          from: `${senderEmail.title} <${senderEmail.address}>`,
          to: email.addresses,
          bcc: financialEmails,
          subject: email.subject,
          text: 'non-html',
          html: replacedData,
        }, async (err, info) => {
          await this.senderHandler(err, info);
          await this.financialComplianceService.updateLastReminder(String(borrower._id));
        });
      });
  }

  async sendFinancialInternal(email: EmailWithFinancial, stopFunding = false) {
    fs.readFile(
      path.resolve(__dirname, '../public/emails', 'financial-internal.html'),
      'utf8',
      async (error, data) => {
        if (error) {
          console.error({ error });
          return;
        }

        const signature = await this.getSignatureAsync(ESenderType.FINANCIAL);

        const text = stopFunding
          ? `<p style="color: #ff0000;"><b>The table below sets out the status of borrowers who have failed to paid their interest statement.</b></p>`
          : `<p><b>The following Borrowers have not paid in, or requested to add their statement to the loan:</b></p>`;

        const borrowerTables = email.borrowers.reduce((allBorrowers, borrower) => {

          return `
            ${allBorrowers}
            <tr>
              <td style="border: 1px solid black; text-align: center">${borrower.borrower.name}</td>
              <td style="border: 1px solid black; text-align: right">${new Intl.NumberFormat('us-US').format(borrower.amountOwed)}</td>
              <td style="border: 1px solid black; text-align: right">${new Intl.NumberFormat('us-US').format(borrower.amountReceived)}</td>
              <td style="border: 1px solid black; text-align: right">${new Intl.NumberFormat('us-US').format(borrower.balanceOutstanding)}</td>
              <td style="border: 1px solid black; text-align: center">${dayjs(new Date(borrower.lastReminderSentAt)).format('M/D/YYYY')}</td>
            </tr>`;

        }, '');

        const borrowersTable = `
            <br>
            <table style="width:100%; border: 1px solid black; border-spacing: 0">
              <tr>
                <th style="border: 2px solid black; text-align: center">Borrower Name</th>
                <th style="border: 2px solid black; text-align: right">Original Amount</th>
                <th style="border: 2px solid black; text-align: right">Amount Paid</th>
                <th style="border: 2px solid black; text-align: right">Amount Due</th>
                <th style="border: 2px solid black; text-align: center">Last notification</th>
              </tr>
            ${borrowerTables}
            </table>`;

        const replacedData = data
          .replace('${text}', text)
          .replace('${borrowerTable}', borrowersTable)
          .replace('${signature}', signature);

        try {
          this.transporter.sendMail({
            from: this.getSender(ESenderType.FINANCIAL),
            to: email.addresses,
            bcc: this.config.hiddenRecipient,
            subject: email.subject,
            text: 'non-html',
            html: replacedData,
          }, async (err, info) => {
            await this.senderHandler(err, info);
          });
        } catch (e) {
          this.sentryService.catchErrorBySentry(null, e);
        }
      });
  }

  private async getEmailStatementParams(borrowerId: string) {
    const financialSettings = await this.financialComplianceService.getFinancialComplianceSettings();
    const financialEmails = financialSettings.financialEmails
      .reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);

    const borrower = await this.borrowerService.getBorrowerById(borrowerId);
    const organization = await this.organizationsService.getOrganizationForBorrower(borrowerId);
    const products = await this.loanChargesService.getLoanProducts(borrower._id.toString());
    const borrowerHasRevolver = products.filter((product) => product.type === ELoanTypes.REVOLVER && isProductActive(product.startDate, product.deactivationDate, new Date())).length > 0;
    const textPath = path.resolve(__dirname, '../public/emails', organization.subfolder, borrowerHasRevolver ? 'interest-statement.html' : 'interest-statement-only-term.html');
    const text = await fs.promises.readFile(textPath, 'utf8');
    const signature = await this.getSignatureAsync(ESenderType.FINANCIAL, organization.subfolder);

    const borrowerRepresentation = borrower
      ? borrower.title
      : borrower.code;

    const complianceBorrowersService = this.getComplianceBorrowersService();
    const borrowerEmails = await complianceBorrowersService.getBorrowerEmailsGrouped(String(borrower._id));

    const mappedEmails = [
      ...borrowerEmails.mainEmails.map((email) => email.email),
      ...borrowerEmails.financialEmails.map((email) => email.email),
    ];

    const replacedText = text
      .replaceAll('${BORROWER_NAME}', borrowerRepresentation)
      .replaceAll('${MONTH}', dayjs().subtract(1, 'month').format('MMMM'))
      .replaceAll('${signature}', signature);

    return { financialEmails, mappedEmails, replacedText };
  }

  async sendEmailWithAttach(borrowerCode: string, files: IMulterFile[]) {
    try {
      const attachments = files.map((file) => ({
        filename: file.originalname,
        path: path.resolve(__dirname, '../uploads', file.filename),
      }));

      const { financialEmails, mappedEmails, replacedText } = await this.getEmailStatementParams(borrowerCode);

      this.transporter.sendMail({
        from: this.getSender(ESenderType.FINANCIAL),
        to: mappedEmails,
        bcc: financialEmails,
        subject: `${borrowerCode} INTEREST STATEMENT`,
        text: 'non-html',
        html: replacedText,
        attachments,
      }, async (err, info) => {
        await this.senderHandler(err, info);
        await Promise.all(attachments.map(async (file) => {
          fs.unlink(file.path, (err) => {
            if (err) {
              console.error(err);
            } else {
              console.log('deleted');
            }
          });
        }));
      });
    } catch (e) {
      this.sentryService.catchErrorBySentry(null, e);
    }
  }

  async sendEmailWithStatement(borrowerId: string, statement: { filename: string; content: Buffer }) {
    try {
      const borrower = await this.borrowerService.getBorrowerById(borrowerId);
      if (!borrower) {
        return;
      }
      const organization = await this.organizationsService.getOrganizationForBorrower(borrowerId);
      const sender = await this.organizationEmailsService.getEmail(organization._id.toString(), OrganizationEmailsType.FINANCIAL);

      const attachments = [statement];

      const { financialEmails, mappedEmails, replacedText } = await this.getEmailStatementParams(borrowerId);

      this.transporter.sendMail({
        from: `${sender.title} <${sender.address}>`,
        to: mappedEmails,
        bcc: financialEmails,
        subject: `${borrower.code} INTEREST STATEMENT`,
        text: 'non-html',
        html: replacedText,
        attachments,
      }, async (err, info) => {
        await this.senderHandler(err, info);
      });
    } catch (e) {
      this.sentryService.catchErrorBySentry(null, e);
    }
  }

  async sendEmailWithSimpleAttachments(email: IEmail, attachments: any[]) {
    const textPath = path.resolve(__dirname, '../public/emails', 'simple-mail.html');
    const text = await fs.promises.readFile(textPath, 'utf8');
    const signature = await this.getSignatureAsync(ESenderType.GOAT);
    const replacedData = text
      .replace('${emailText}', '<p>Here’s your file:</p>')
      .replace('${signature}', signature);
    try {
      this.transporter.sendMail({
        from: this.getSender(ESenderType.REGULAR),
        to: email.addresses,
        bcc: this.config.hiddenRecipient,
        subject: email.subject,
        text: email.subject,
        html: replacedData,
        attachments,
      }, async (err, info) => {
        await this.senderHandler(err, info);
      });
    } catch (e) {
      this.sentryService.catchErrorBySentry(null, e);
    }
  }

  async sendLoanUploadReport(email: IEmail, attachments: any[], borrowerId: string) {
    const organization = await this.organizationsService.getOrganizationForBorrower(borrowerId);
    const textPath = path.resolve(__dirname, '../public/emails', organization.subfolder, 'loan-upload.html');
    const text = await fs.promises.readFile(textPath, 'utf8');
    const signature = await this.getSignatureAsync(ESenderType.GOAT, organization.subfolder);
    const senderEmail = await this.organizationEmailsService.getEmail(organization._id.toString(), OrganizationEmailsType.FINANCIAL);
    const replacedData = text
      .replace('${emailText}', `<p>${email.text}</p>`)
      .replace('${signature}', signature);
    try {
      this.transporter.sendMail({
        from: `${senderEmail.title} <${senderEmail.address}>`,
        to: email.addresses,
        cc: `${senderEmail.title} <${senderEmail.address}>`,
        subject: email.subject,
        text: email.subject,
        html: replacedData,
        attachments,
      }, async (err, info) => {
        await this.senderHandler(err, info);
      });
    } catch (e) {
      this.sentryService.catchErrorBySentry(null, e);
    }
  }

  async sendPlaidReminder(email: IEmail) {
    const textPath = path.resolve(__dirname, '../public/emails', 'plaid-reminder.html');
    const text = await fs.promises.readFile(textPath, 'utf8');
    const signature = await this.getSignatureAsync(ESenderType.FINANCIAL);
    const replacedData = text
      .replace('${signature}', signature);
    try {
      this.transporter.sendMail({
        from: this.getSender(ESenderType.REGULAR),
        to: email.addresses,
        subject: `🔴 ${email.subject}`,
        cc: this.getSender(ESenderType.REGULAR),
        text: email.subject,
        html: replacedData,
        headers: {
          'priority': 'high',
        },
        attachments: [{
          filename: 'plaid-reminder-instruction.png',
          path: process.cwd() + '/public/images/plaid-reminder-instruction.png',
          cid: 'plaid-instruction',
        }],
      }, async (err, info) => {
        await this.senderHandler(err, info);
      });
    } catch (e) {
      this.sentryService.catchErrorBySentry(null, e);
    }
  }

  async sendEmailFTP(email: Partial<IEmail>, attachments: any[], success: boolean) {
    const textPath = path.resolve(__dirname, '../public/emails', 'simple-mail.html');
    const text = await fs.promises.readFile(textPath, 'utf8');
    const signature = await this.getSignatureAsync(ESenderType.GOAT);
    const emailText = success ? 'the attached file was uploaded to the FTP site' : 'the attached file was NOT uploaded to the FTP site';
    const replacedData = text
      .replace('${emailText}', `<p>${emailText}</p>`)
      .replace('${signature}', signature);
    try {
      this.transporter.sendMail({
        from: this.getSender(ESenderType.FINANCIAL),
        to: this.config.hiddenRecipient,
        subject: email.subject,
        text: emailText,
        html: replacedData,
        attachments,
      }, async (err, info) => {
        await this.senderHandler(err, info);
      });
    } catch (e) {
      this.sentryService.catchErrorBySentry(null, e);
    }
  }

  async sendBrokerEmail(email: Partial<IEmail>, attachments: any[], replacements: { broker: string; month: string }) {
    const textPath = path.resolve(__dirname, '../public/emails', 'broker-report.html');
    const text = await fs.promises.readFile(textPath, 'utf8');
    const signature = await this.getSignatureAsync(ESenderType.FINANCIAL_ONLY);
    const replacedData = text
      .replace('${BORROWER_NAME}', replacements.broker)
      .replace('${MONTH}', replacements.month)
      .replace('${signature}', signature);
    try {
      this.transporter.sendMail({
        from: this.getSender(ESenderType.FINANCIAL),
        to: [...this.config.hiddenRecipient, ...email.addresses],
        cc: this.getSender(ESenderType.FINANCIAL),
        subject: email.subject,
        text: replacedData,
        html: replacedData,
        attachments,
      }, async (err, info) => {
        await this.senderHandler(err, info);
      });
    } catch (e) {
      this.sentryService.catchErrorBySentry(null, e);
    }
  }

  async sendProspectForm(email: Partial<IEmail>, replacements: {
    link: string,
    contactName: string,
    prospectName: string,
    password: string,
  }, stage: 'one' | 'two') {
    const stageTemplate = stage === 'one' ? 'prospect-form-one' : 'prospect-form-two';
    const textPath = path.resolve(__dirname, '../public/emails', `${stageTemplate}.html`);
    const text = await fs.promises.readFile(textPath, 'utf8');
    const signature = await this.getSignatureAsync(ESenderType.NEW_BUSINESS);
    const replacedData = text
      .replaceAll('${LINK}', replacements.link)
      .replaceAll('${CONTACT_NAME}', replacements.contactName)
      .replaceAll('${PASSWORD}', replacements.password)
      .replace('${signature}', signature);
    try {
      this.transporter.sendMail({
        from: this.getSender(ESenderType.NEW_BUSINESS),
        to: [...email.addresses],
        cc: this.getSender(ESenderType.NEW_BUSINESS, `: ${replacements.prospectName}`),
        subject: email.subject,
        text: replacedData,
        html: replacedData,
      }, async (err, info) => {
        await this.senderHandler(err, info);
      });
    } catch (e) {
      this.sentryService.catchErrorBySentry(null, e);
    }
  }

  async sendProspectReminder(email: Partial<IEmail>, replacements: {
    link: string,
    contactName: string,
    hasEmptyFields: boolean,
    requiredGroups: string[],
    prospectName: string,
    password: string,
  }) {
    const textPath = path.resolve(__dirname, '../public/emails', 'prospect-form-reminder.html');
    const text = await fs.promises.readFile(textPath, 'utf8');
    const signature = await this.getSignatureAsync(ESenderType.NEW_BUSINESS);
    const fileInfo = replacements.requiredGroups.length > 0
      ? ` and upload missing documents under the following sections: ${replacements.requiredGroups.join(', ')}`
      : '';
    const replacedData = text
      .replaceAll('${LINK}', replacements.link)
      .replaceAll('${CONTACT_NAME}', replacements.contactName)
      .replace('${HAS_EMPTY_FIELDS}', replacements.hasEmptyFields ? ' and complete all sections of the form' : '')
      .replace('${PASSWORD}', replacements.password)
      .replace('${FILE_INFO}', fileInfo)
      .replace('${signature}', signature);
    try {
      this.transporter.sendMail({
        from: this.getSender(ESenderType.NEW_BUSINESS),
        to: [...email.addresses],
        cc: this.getSender(ESenderType.NEW_BUSINESS, `: ${replacements.prospectName}`),
        subject: email.subject,
        text: replacedData,
        html: replacedData,
      }, async (err, info) => {
        await this.senderHandler(err, info);
      });
    } catch (e) {
      this.sentryService.catchErrorBySentry(null, e);
    }
  }
}
