"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FinancialComplianceService = void 0;
const dayjs_1 = __importDefault(require("dayjs"));
const decimal_js_1 = __importDefault(require("decimal.js"));
const Borrower_model_1 = require("../models/Borrower.model");
const microservice_tasks_db_1 = require("../db/microservice-tasks.db");
const microservice_task_enum_1 = require("../enums/microservice-task.enum");
const BorrowerCompliance_model_1 = require("../models/BorrowerCompliance.model");
const FinancialComplianceBorrower_model_1 = __importDefault(require("../models/FinancialComplianceBorrower.model"));
const FinancialCompliance_model_1 = __importDefault(require("../models/FinancialCompliance.model"));
class FinancialComplianceService {
    globalsRepository;
    globalsService;
    getLoanStatementService;
    getNodemailerService;
    organizationsService;
    constructor(globalsRepository, globalsService, getLoanStatementService, getNodemailerService, organizationsService) {
        this.globalsRepository = globalsRepository;
        this.globalsService = globalsService;
        this.getLoanStatementService = getLoanStatementService;
        this.getNodemailerService = getNodemailerService;
        this.organizationsService = organizationsService;
    }
    async getFinancialComplianceSettings() {
        return await FinancialCompliance_model_1.default.findOne({}) ?? await new FinancialCompliance_model_1.default({}).save();
    }
    async updateSettings(newSettings) {
        try {
            return await FinancialCompliance_model_1.default.findOneAndUpdate({}, { ...newSettings }, { new: true });
        }
        catch (e) {
            console.error({ e });
            return null;
        }
    }
    async refreshFinancial() {
        try {
            const newSettings = {
                invoiceSentDate: null,
                dueDate: (0, dayjs_1.default)().date(7).startOf('day').toDate(),
                stopFundingDate: (0, dayjs_1.default)().date(10).startOf('day').toDate(),
            };
            await this.updateSettings(newSettings);
            const newBorrowerSettings = {
                invoiceSentDate: null,
                dueDate: null,
                stopFundingDate: null,
                lastReminderSentAt: null,
                accountClearedDate: null,
                amountOwed: 0,
                addToLoan: null,
                willPayIn: null,
                transferDone: null,
                amountReceived: 0,
                balanceOutstanding: 0,
                reminderCounter: 0,
            };
            const borrowers = await this.getAllFinancialComplianceBorrowers();
            await Promise.all(borrowers.map(async (borrower) => {
                await this.updateBorrower(String(borrower._id), newBorrowerSettings);
            }));
            return { message: 'refreshed' };
        }
        catch (e) {
            console.error(e);
            return { message: 'error', e };
        }
    }
    async refillFinancial() {
        const borrowers = await FinancialComplianceBorrower_model_1.default.find().lean();
        for (const borrower of borrowers) {
            const loanStatementService = this.getLoanStatementService();
            const complianceData = await loanStatementService.getBorrowerComplianceData(borrower.borrower.toString(), new Date());
            if (complianceData.total > 0) {
                const foundBorrower = await FinancialComplianceBorrower_model_1.default.findById(borrower._id).lean();
                if (foundBorrower) {
                    await FinancialComplianceBorrower_model_1.default.findByIdAndUpdate(borrower._id, {
                        amountOwed: complianceData.total,
                        dueDate: complianceData.dueDate,
                        amountReceived: complianceData.totalPaid,
                        balanceOutstanding: new decimal_js_1.default(complianceData.total).sub(complianceData.totalPaid).toNumber(),
                    });
                }
            }
        }
        return null;
    }
    async sendFinancialReminders(ids = null) {
        const reminderBorrowers = await this.sendExternalReminder(ids, true);
        setTimeout(async () => {
            await this.sendInternalReminders(reminderBorrowers.remindedBorrowers, reminderBorrowers.stopFundingBorrowers);
        }, 60000);
    }
    async sendExternalReminder(ids = null, ignoreRules) {
        const financialSettings = await FinancialCompliance_model_1.default.findOne({});
        const financeBorrowers = ids
            ? await FinancialComplianceBorrower_model_1.default.find({ _id: { $in: ids } }).populate('borrower')
            : await FinancialComplianceBorrower_model_1.default.find({}).populate('borrower');
        const stopFundingBorrowers = [];
        const remindedBorrowers = [];
        await Promise.all(financeBorrowers.map(async (b) => {
            const nodemailerService = this.getNodemailerService();
            const fullBorrower = b.toObject();
            const organization = await this.organizationsService.getOrganizationForBorrower(fullBorrower.borrower._id.toString());
            fullBorrower.invoiceSentDate = fullBorrower.invoiceSentDate ?? financialSettings.invoiceSentDate;
            fullBorrower.stopFundingDate = fullBorrower.stopFundingDate ?? financialSettings.stopFundingDate;
            fullBorrower.dueDate = fullBorrower.dueDate ?? financialSettings.dueDate;
            const foundBorrower = await BorrowerCompliance_model_1.BorrowerCompliance.findOne({ borrower: fullBorrower.borrower._id });
            if (!foundBorrower.isEmailingActive) {
                return;
            }
            fullBorrower.mainEmails = foundBorrower.mainEmails ?? [];
            fullBorrower.financialEmails = foundBorrower.financialEmails ?? [];
            const currentDate = (0, dayjs_1.default)();
            const stopFundingDate = (0, dayjs_1.default)(fullBorrower.stopFundingDate);
            const invoiceSentDate = (0, dayjs_1.default)(fullBorrower.invoiceSentDate);
            const isTodayStopFundingDate = currentDate.isSame(stopFundingDate, 'day');
            if (fullBorrower.balanceOutstanding > 0 && isTodayStopFundingDate && !b.transferDone) {
                const email = {
                    text: null,
                    subject: `${organization.name.toUpperCase()}: ${b.borrower.code} UNPAID Statement ${new Intl.NumberFormat('us-US').format(fullBorrower.balanceOutstanding)} – ${(0, dayjs_1.default)().subtract(1, 'month').format('MMMM')}`,
                    addresses: fullBorrower.mainEmails.map((email) => email.email),
                    customData: fullBorrower,
                };
                await nodemailerService.sendFinancialExternal(email, organization._id.toString(), true);
                stopFundingBorrowers.push(fullBorrower);
            }
            if (!isTodayStopFundingDate && (ignoreRules || (currentDate.diff(invoiceSentDate, 'day') >= 3 && currentDate.diff(stopFundingDate, 'day') < 0))) {
                const email = {
                    text: null,
                    subject: `${organization.name.toUpperCase()}: ${b.borrower.code} Statement ${new Intl.NumberFormat('us-US').format(fullBorrower.balanceOutstanding)} – ${(0, dayjs_1.default)().subtract(1, 'month').format('MMMM')}`,
                    addresses: [
                        ...fullBorrower.financialEmails.map((email) => email.email),
                        ...fullBorrower.mainEmails.map((email) => email.email),
                    ],
                    customData: fullBorrower,
                };
                await nodemailerService.sendFinancialExternal(email, organization._id.toString());
            }
            if ((currentDate.diff(invoiceSentDate, 'day') > 0 && currentDate.diff(stopFundingDate, 'day') < 0)
                || fullBorrower.addToLoan) {
                remindedBorrowers.push(fullBorrower);
            }
        }));
        return { remindedBorrowers, stopFundingBorrowers };
    }
    async sendInternalReminders(remindedBorrowers, stopFundingBorrowers) {
        try {
            const financialSettings = await FinancialCompliance_model_1.default.findOne({});
            const remindedBorrowersUpdated = await Promise.all(remindedBorrowers.map(async (b) => {
                const updatedBorrower = await FinancialComplianceBorrower_model_1.default.findOne({ _id: b._id });
                b.lastReminderSentAt = updatedBorrower.lastReminderSentAt;
                b.reminderCounter = updatedBorrower.reminderCounter;
                return b;
            }));
            const stopFundingBorrowersUpdated = await Promise.all(stopFundingBorrowers.map(async (b) => {
                const updatedBorrower = await FinancialComplianceBorrower_model_1.default.findOne({ _id: b._id });
                b.lastReminderSentAt = updatedBorrower.lastReminderSentAt;
                b.reminderCounter = updatedBorrower.reminderCounter;
                return b;
            }));
            const nodemailerService = this.getNodemailerService();
            if (remindedBorrowersUpdated.length) {
                const financialEmails = financialSettings.financialEmails
                    .reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);
                const email = {
                    text: null,
                    subject: 'UNPAID INTEREST STATEMENTS',
                    addresses: financialEmails,
                    borrowers: remindedBorrowersUpdated,
                };
                await nodemailerService.sendFinancialInternal(email);
            }
            if (stopFundingBorrowersUpdated.length) {
                const managementAddresses = financialSettings.managementEmails
                    .reduce((acc, email) => email.isActive ? [...acc, email.email] : acc, []);
                const email = {
                    text: null,
                    subject: 'UNPAID INTEREST STATEMENTS – POTENTIAL STOP FUNDING',
                    addresses: managementAddresses,
                    borrowers: stopFundingBorrowersUpdated,
                };
                await nodemailerService.sendFinancialInternal(email, true);
            }
        }
        catch (e) {
            console.error({ e });
        }
    }
    async createEmptyBorrower(borrowerId) {
        const foundBorrower = await FinancialComplianceBorrower_model_1.default.findOne({ borrower: borrowerId });
        if (!foundBorrower) {
            await new FinancialComplianceBorrower_model_1.default({ borrower: borrowerId }).save();
        }
    }
    async removeBorrower(borrowerId) {
        await FinancialComplianceBorrower_model_1.default.findByIdAndDelete(borrowerId);
    }
    async getAllFinancialComplianceBorrowers() {
        const borrowers = await FinancialComplianceBorrower_model_1.default
            .find({})
            .populate('borrower')
            .lean();
        const borrowersWithEmailingStatus = await Promise.all(borrowers.map(async (borrower) => {
            if (!borrower.borrower.active) {
                return;
            }
            const foundBorrower = await BorrowerCompliance_model_1.BorrowerCompliance.findOne({ borrower: borrower.borrower._id });
            return {
                ...borrower,
                isEmailingActive: foundBorrower?.isEmailingActive ?? true,
                isDailyTransactionsEmailingActive: foundBorrower?.isDailyTransactionsEmailingActive ?? true,
            };
        }));
        return borrowersWithEmailingStatus
            .filter((borrower) => !!borrower)
            .sort((a, b) => a.borrower.code > b.borrower.code ? 1 : -1);
    }
    async updateBorrower(id, updatedBorrower) {
        try {
            updatedBorrower.amountOwed = new decimal_js_1.default(updatedBorrower.amountOwed).toDP().toNumber();
            updatedBorrower.amountReceived = new decimal_js_1.default(updatedBorrower.amountReceived).toDP().toNumber();
            updatedBorrower.balanceOutstanding = Math.max(0, new decimal_js_1.default(updatedBorrower.amountOwed).sub(updatedBorrower.amountReceived).toNumber());
            if ((updatedBorrower.amountOwed === updatedBorrower.amountReceived) && (updatedBorrower.amountOwed > 0) && !updatedBorrower.accountClearedDate) {
                updatedBorrower.accountClearedDate = new Date();
            }
            return await FinancialComplianceBorrower_model_1.default.findByIdAndUpdate(id, { ...updatedBorrower }, { new: true });
        }
        catch (e) {
            console.error({ e });
            return null;
        }
    }
    async updateLastReminder(financialBorrowerId) {
        try {
            const borrower = await FinancialComplianceBorrower_model_1.default.findById(financialBorrowerId);
            await FinancialComplianceBorrower_model_1.default.findByIdAndUpdate(financialBorrowerId, {
                lastReminderSentAt: new Date(),
                reminderCounter: borrower.reminderCounter + 1,
            });
        }
        catch (e) {
            console.error({ e });
        }
    }
    async sendPlaidReminders() {
        const settings = await this.globalsService.getGlobalSetting('plaidReminded');
        if (settings && settings.plaidReminded) {
            return null;
        }
        const complianceBorrowers = await BorrowerCompliance_model_1.BorrowerCompliance.find().select('+plaidAccessToken').lean();
        const borrowersWithoutTokes = complianceBorrowers.filter((c) => !c.plaidAccessToken);
        const notifiedBorrowers = [];
        await Promise.all(borrowersWithoutTokes.map(async (complianceBorrower) => {
            const borrower = await Borrower_model_1.BorrowerModel.findById(complianceBorrower.borrower);
            if (complianceBorrower.isEmailingActive && borrower.active) {
                const mainEmails = complianceBorrower.mainEmails.map((mainEmail) => mainEmail.email.trim());
                const financialEmails = complianceBorrower.financialEmails.map((financialEmail) => financialEmail.email.trim());
                const allEmails = Array.from(new Set([...mainEmails, ...financialEmails]));
                const nodemailerService = this.getNodemailerService();
                await nodemailerService.sendPlaidReminder({
                    addresses: allEmails,
                    subject: 'Loan Requirement: online read-only bank access',
                    text: '',
                });
                notifiedBorrowers.push(borrower.name);
            }
        }));
        await this.globalsRepository.update({ plaidReminded: true });
        await (0, microservice_tasks_db_1.createMicroserviceTasks)(microservice_task_enum_1.EMicroserviceTask.ENABLE_PLAID_NOTIFICATION, {}, (0, dayjs_1.default)(new Date()).add(1, 'day').toDate());
        return notifiedBorrowers.sort();
    }
    async getFinancialComplianceBorrower(borrowerId) {
        return FinancialComplianceBorrower_model_1.default.findOne({ borrower: borrowerId }).lean();
    }
}
exports.FinancialComplianceService = FinancialComplianceService;
