"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LoanProductsService = void 0;
const mongoose_1 = __importDefault(require("mongoose"));
const dayjs_1 = __importDefault(require("dayjs"));
const decimal_js_1 = __importDefault(require("decimal.js"));
const LoanProducts_model_1 = require("../models/LoanProducts.model");
const LoanCharges_model_1 = require("../models/LoanCharges.model");
const loan_payments_service_1 = require("./loan-payments.service");
const _models_1 = require("../models/_models");
const LoanTransaction_model_1 = require("../models/LoanTransaction.model");
const loan_statement_db_1 = require("../db/loan-statement.db");
const loan_products_db_1 = require("../db/loan-products.db");
class LoanProductsService {
    loanChargesService;
    borrowersDB;
    constructor(loanChargesService, borrowersDB) {
        this.loanChargesService = loanChargesService;
        this.borrowersDB = borrowersDB;
    }
    async getLinkedLoanProducts(codes) {
        const masterProducts = await LoanProducts_model_1.LoanProduct.find({ code: { $in: codes } });
        const masterProductIds = masterProducts.map((product) => product._id);
        const loanProducts = await LoanProducts_model_1.LoanProduct
            .find({ masterLoanProductId: { $in: masterProductIds } })
            .sort({ code: 1 })
            .lean();
        return loanProducts.reduce((acc, loanProduct) => ({ ...acc, [loanProduct.code]: loanProduct._id.toString() }), {});
    }
    async getProductsWithBalances(products, showFloatedBalance) {
        await Promise.all(products.map(async (product, index) => {
            const balances = await this.loanChargesService.getLoanProductBalance(product._id.toString());
            products[index].balance = balances.balance;
            products[index].floatedBalance = showFloatedBalance ? balances.floatedBalance : 0;
        }));
        return products;
    }
    async getProductPaymentOrder(productId) {
        const product = await LoanProducts_model_1.LoanProduct.findById(productId).lean();
        const charges = await LoanCharges_model_1.LoanCharge.find({ productId }).lean();
        if (product.isDefaultPaymentOrder) {
            const chargeOrders = charges.map((charge) => {
                const foundIndex = loan_payments_service_1.paymentOrder.findIndex((order) => order === charge.chargeType);
                return { chargeId: charge._id.toString(), order: foundIndex };
            });
            const sortedChargeOrders = chargeOrders.sort((a, b) => a.order - b.order);
            return sortedChargeOrders.map((item) => item.chargeId);
        }
        const sortedCharges = charges.sort((a, b) => {
            return (a.paymentOrder ?? 0) - (b.paymentOrder ?? 0);
        });
        return sortedCharges.map((charge) => charge._id.toString());
    }
    async savePaymentOrder(productId, paymentOrder) {
        if (paymentOrder === null || paymentOrder.length === 0) {
            await LoanProducts_model_1.LoanProduct.findByIdAndUpdate(productId, { isDefaultPaymentOrder: true });
        }
        else {
            await LoanProducts_model_1.LoanProduct.findByIdAndUpdate(productId, { isDefaultPaymentOrder: false });
            await Promise.all(paymentOrder.map(async (chargeId, index) => {
                await LoanCharges_model_1.LoanCharge.findByIdAndUpdate(chargeId, { paymentOrder: index });
            }));
        }
    }
    async getAllLoanProducts(borrowerIds) {
        if (!borrowerIds) {
            const borrowers = await this.borrowersDB.getActiveBorrowers();
            borrowerIds = borrowers.map((borrower) => borrower._id.toString());
        }
        return LoanProducts_model_1.LoanProduct.aggregate([
            { $match: { borrowerId: { $in: borrowerIds.map((borrowerId) => new mongoose_1.default.Types.ObjectId(borrowerId)) } } },
            {
                $lookup: {
                    from: _models_1.MODEL_NAMES.borrowers,
                    localField: 'borrowerId',
                    foreignField: '_id',
                    as: 'borrower',
                },
            },
            { $unwind: '$borrower' },
            { $sort: { 'borrower.name': 1 } },
            { $project: { borrower: 0 } },
        ]);
    }
    ;
    async getLoanProductBalance(productId, forDate = new Date()) {
        const simpleTransactions = await LoanTransaction_model_1.LoanTransaction.aggregate([
            {
                $match: {
                    productId: new mongoose_1.default.Types.ObjectId(productId),
                    date: { $lte: (0, dayjs_1.default)(forDate).startOf('day').subtract(1, 'seconds').toDate() },
                },
            },
            { $sort: { date: -1, order: -1, createdAt: -1 } },
            { $limit: 1 },
        ]);
        const collectionsTransactions = await LoanTransaction_model_1.LoanTransaction.aggregate([
            {
                $match: {
                    productId: new mongoose_1.default.Types.ObjectId(productId),
                    effectiveDate: { $lte: forDate },
                    $expr: { $ne: ['$date', '$effectiveDate'] },
                },
            },
            { $sort: { effectiveDate: -1, order: -1, createdAt: -1 } },
            { $limit: 1 },
        ]);
        const normalTransactions = await LoanTransaction_model_1.LoanTransaction.aggregate([
            {
                $match: {
                    productId: new mongoose_1.default.Types.ObjectId(productId),
                    effectiveDate: { $lte: forDate },
                    $expr: { $eq: ['$date', '$effectiveDate'] },
                },
            },
            { $sort: { effectiveDate: -1, order: -1, createdAt: -1 } },
            { $limit: 1 },
        ]);
        return this.calculateBalanceFromTransactions(simpleTransactions.length > 0 ? simpleTransactions[0] : null, normalTransactions.length > 0 ? normalTransactions[0] : null, collectionsTransactions.length > 0 ? collectionsTransactions[0] : null, productId, forDate);
    }
    ;
    async calculateBalanceFromTransactions(simpleTransaction, normalTransaction, collectionsTransaction, productId, forDate) {
        const balance = simpleTransaction?.balance || 0;
        let floatedBalance = 0;
        if (normalTransaction && collectionsTransaction) {
            const totalCollections = await (0, loan_statement_db_1.getTotalForDate)(productId, collectionsTransaction.effectiveDate);
            if ((0, dayjs_1.default)(normalTransaction.date).isSame(collectionsTransaction.date)) {
                floatedBalance = new decimal_js_1.default(normalTransaction.floatedBalance).add(totalCollections).toNumber();
            }
            else if ((0, dayjs_1.default)(normalTransaction.date).isAfter(collectionsTransaction.date)) {
                floatedBalance = normalTransaction.floatedBalance;
            }
            else {
                floatedBalance = collectionsTransaction.balance;
            }
        }
        else if (normalTransaction) {
            floatedBalance = normalTransaction.floatedBalance;
        }
        else if (!normalTransaction && !collectionsTransaction) {
            const totalPostponed = await (0, loan_products_db_1.getPostponedTransactions)(forDate, productId);
            return { balance: 0, floatedBalance: totalPostponed };
        }
        return { balance, floatedBalance };
    }
    ;
    async getLoanProductBalances(productIds, forDate) {
        const results = {};
        await Promise.all(productIds.map(async (productId) => {
            results[productId] = await this.getLoanProductBalance(productId, forDate);
        }));
        return results;
    }
    ;
}
exports.LoanProductsService = LoanProductsService;
