"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LoanStatementBalanceService = void 0;
const decimal_js_1 = __importDefault(require("decimal.js"));
const dayjs_1 = __importDefault(require("dayjs"));
const LoanPayment_model_1 = require("../models/LoanPayment.model");
const LoanStatementTransaction_model_1 = require("../models/LoanStatementTransaction.model");
const loan_charge_type_enum_1 = require("../enums/loan-charge-type.enum");
class LoanStatementBalanceService {
    loanChargesService;
    getLoanStatementService;
    loanTransactionsService;
    constructor(loanChargesService, getLoanStatementService, loanTransactionsService) {
        this.loanChargesService = loanChargesService;
        this.getLoanStatementService = getLoanStatementService;
        this.loanTransactionsService = loanTransactionsService;
    }
    async cleanDecreasePayment(payment, userId) {
        if (!!payment.paid) {
            let firstPaidStatement = null;
            await Promise.all(payment.paid.map(async (paid) => {
                const foundPayment = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.findById(paid.statementId);
                if (!foundPayment) {
                    return;
                }
                if (!firstPaidStatement ||
                    (foundPayment.date.getTime() < firstPaidStatement.date.getTime()) ||
                    (firstPaidStatement.date.getTime() === foundPayment.date.getTime() && firstPaidStatement.order > foundPayment.order)) {
                    firstPaidStatement = foundPayment;
                }
                await LoanStatementTransaction_model_1.LoanStatementTransactionModel.findByIdAndUpdate(paid.statementId, {
                    amountPaid: new decimal_js_1.default(foundPayment.amountPaid).sub(paid.amount).toNumber(),
                    settlementCode: null,
                });
            }));
            if (firstPaidStatement) {
                await this.calculateProductBalance(payment.productId.toString(), firstPaidStatement.date);
            }
        }
        if (!!payment.loanTransactions) {
            await Promise.all(payment.loanTransactions.map(async (t) => {
                await this.loanTransactionsService.deleteLoanTransaction(t.transactionId.toString(), false, userId);
            }));
        }
        await LoanPayment_model_1.LoanPaymentModel.findByIdAndUpdate(payment._id, { paid: [] });
    }
    async calculateProductBalance(productId, startDate) {
        const loanStatementService = this.getLoanStatementService();
        const statements = await loanStatementService.getTransactionsFromDate(productId, startDate);
        for (const statement of statements) {
            await this.updateBalance(statement);
        }
    }
    async updateBalance(loanStatementTransaction) {
        const loanStatementService = this.getLoanStatementService();
        const previousTransaction = await loanStatementService.getPreviousTransaction(loanStatementTransaction);
        const previousBalance = !previousTransaction ? 0 : previousTransaction.balance;
        const newBalance = new decimal_js_1.default(previousBalance).add(loanStatementTransaction.amount).sub(loanStatementTransaction.amountPaid).toNumber();
        await LoanStatementTransaction_model_1.LoanStatementTransactionModel.findByIdAndUpdate(loanStatementTransaction._id, { balance: newBalance });
    }
    async getLastStatementTransactionForDate(productId, startDate) {
        const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
        const lastStatements = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
            {
                $match: {
                    'chargeId': { $in: charges.map((charge) => charge._id) },
                    'date': { $lt: startDate },
                },
            }, {
                $sort: {
                    'date': -1,
                    'order': -1,
                },
            }, {
                $limit: 1,
            },
        ]);
        if (lastStatements.length === 0) {
            return null;
        }
        return lastStatements[0];
    }
    async getNextAdminTransactionForDate(productId, date) {
        const charges = await this.loanChargesService.getLoanChargeForProduct(productId);
        const adminCharge = charges.find((c) => c.chargeType === loan_charge_type_enum_1.ELoanChargeType.ADMIN_FEE);
        if (adminCharge) {
            const nextAdminFee = await LoanStatementTransaction_model_1.LoanStatementTransactionModel
                .findOne({
                chargeId: adminCharge._id,
                date: { $gte: (0, dayjs_1.default)(date).startOf('month').toDate() },
            })
                .sort({ date: 1 })
                .lean();
            if (nextAdminFee) {
                const totalDays = (0, dayjs_1.default)(date).daysInMonth();
                const day = (0, dayjs_1.default)(date).date();
                return new decimal_js_1.default(day).div(totalDays).mul(nextAdminFee.amount).toDP(2).toNumber();
            }
        }
        return 0;
    }
}
exports.LoanStatementBalanceService = LoanStatementBalanceService;
