"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPeriods = exports.getBorrowerWithPeriods = exports.getStatementBorrowersTransactions = exports.getTotalForDate = exports.getTotalTransactionAmountForCharge = exports.ledgerHeadersMap = exports.ELedgerReportType = void 0;
const lodash_1 = __importDefault(require("lodash"));
const mongoose_1 = __importDefault(require("mongoose"));
const dayjs_1 = __importDefault(require("dayjs"));
const LoanStatementTransaction_model_1 = require("../models/LoanStatementTransaction.model");
const LoanTransaction_model_1 = require("../models/LoanTransaction.model");
const loan_products_db_1 = require("./loan-products.db");
const LoanProducts_model_1 = require("../models/LoanProducts.model");
const Borrower_model_1 = require("../models/Borrower.model");
const loan_transactions_db_1 = require("./loan-transactions.db");
const reports_db_1 = require("./reports.db");
const decimal_js_1 = __importDefault(require("decimal.js"));
var ELedgerReportType;
(function (ELedgerReportType) {
    ELedgerReportType[ELedgerReportType["SHORT"] = 0] = "SHORT";
    ELedgerReportType[ELedgerReportType["DETAILED"] = 1] = "DETAILED";
})(ELedgerReportType || (exports.ELedgerReportType = ELedgerReportType = {}));
exports.ledgerHeadersMap = {
    [ELedgerReportType.SHORT]: {
        borrowerCode: 'borrower code',
        type: 'type',
        chargeCode: 'BS code',
        PLCode: 'P&L code',
        productCode: 'product code',
        productName: 'product name',
        info: 'memo/reference',
        title: 'title',
        amount: 'amount',
        statementAmount: 'Statement amount',
    },
    [ELedgerReportType.DETAILED]: {
        borrowerCode: 'borrower code',
        date: 'date',
        type: 'type',
        chargeCode: 'BS code',
        PLCode: 'P&L code',
        productCode: 'product code',
        productName: 'product name',
        info: 'memo/reference',
        amount: 'amount',
        statementAmount: 'Statement amount',
        title: 'title',
        balance: 'balance',
        floatedBalance: 'floated balance',
        paymentDate: 'payment date',
    },
};
const getTotalTransactionAmountForCharge = async (chargeId, start, end) => {
    const results = await LoanStatementTransaction_model_1.LoanStatementTransactionModel.aggregate([
        {
            '$match': {
                $and: [
                    { 'date': { $gte: start } },
                    { 'date': { $lte: end } },
                    { 'chargeId': new mongoose_1.default.Types.ObjectId(chargeId) },
                ],
            },
        }, {
            '$group': {
                '_id': '$chargeId',
                'totalAmount': { '$sum': '$amount' },
            },
        }, {
            '$project': {
                '_id': 0,
                'totalAmount': { $round: ['$totalAmount', 2] },
            },
        },
    ]);
    if (results.length === 0) {
        return 0;
    }
    else {
        return results[0].totalAmount;
    }
};
exports.getTotalTransactionAmountForCharge = getTotalTransactionAmountForCharge;
const getTotalForDate = async (productId, date) => {
    const totalAmount = await LoanTransaction_model_1.LoanTransaction.aggregate([
        {
            $match: {
                'productId': new mongoose_1.default.Types.ObjectId(productId),
                'effectiveDate': date,
                $expr: { $ne: ['$date', '$effectiveDate'] },
            },
        }, {
            $group: {
                '_id': '$effectiveDate',
                'totalAmount': { $sum: '$amount' },
            },
        },
    ]);
    if (totalAmount.length === 0) {
        return 0;
    }
    return totalAmount[0].totalAmount;
};
exports.getTotalForDate = getTotalForDate;
const getStatementBorrowersTransactions = async (start, end, selectedPeriod, selectedBorrowerIds, ledgerType) => {
    const header = exports.ledgerHeadersMap[ledgerType];
    if (!selectedBorrowerIds) {
        return [{ transactions: [header] }];
    }
    const periodFull = selectedPeriod === 'ENTIRE_LOAN'
        ? [{
                period: {
                    selectedPeriod: selectedPeriod,
                    start: (0, dayjs_1.default)(start).utc().toDate().toISOString(),
                    end: (0, dayjs_1.default)(end).utc().toDate().toISOString(),
                },
            }]
        : await (0, exports.getBorrowerWithPeriods)(selectedBorrowerIds[0], {
            selectedStart: new Date(start),
            selectedEnd: new Date(end),
            selectedPeriod,
        });
    const allProductIds = [];
    await Promise.all(selectedBorrowerIds.map(async (borrowerId) => {
        const borrowerProducts = await (0, loan_products_db_1.getLoanProducts)(borrowerId);
        allProductIds.push(borrowerProducts.map((product) => product._id.toString()));
    }));
    const showBalances = true;
    const products = await LoanProducts_model_1.LoanProduct.find({ borrowerId: { $in: selectedBorrowerIds.map((id) => new mongoose_1.default.Types.ObjectId(id)) } }).lean();
    const productIds = products.map((product) => product._id.toString());
    const ledgerData = await (0, reports_db_1.getLedger)({
        productIds,
        startDate: new Date((0, dayjs_1.default)(periodFull[0].period.start).utc().toDate().toISOString()),
        endDate: new Date((0, dayjs_1.default)(periodFull[0].period.end).utc().toDate().toISOString()),
        reportType: ledgerType,
    }, { showBalances: showBalances, showFloatedBalance: showBalances });
    const transactions = Object.values(ledgerData).reduce((acc, ledgerTransaction) => [...acc, ...ledgerTransaction], []);
    const transactionsWithoutBalance = showBalances
        ? transactions.map((tr) => {
            return {
                ...tr,
                balance: tr.balance ? new decimal_js_1.default(tr.balance).toDP(2).toNumber() : tr.balance,
                floatedBalance: tr.floatedBalance ? new decimal_js_1.default(tr.floatedBalance).toDP(2).toNumber() : tr.floatedBalance,
            };
        })
        : lodash_1.default.map(transactions, (obj) => lodash_1.default.omit(obj, ['balance', 'floatedBalance']));
    return [{ transactions: [header, ...transactionsWithoutBalance] }];
};
exports.getStatementBorrowersTransactions = getStatementBorrowersTransactions;
const getBorrowerWithPeriods = async (borrowerId, period, splitPeriods = false) => {
    const borrower = await Borrower_model_1.BorrowerModel.findById(borrowerId).lean();
    let start;
    let end;
    switch (period.selectedPeriod) {
        case 'SELECTED_PERIOD': {
            start = (0, dayjs_1.default)(new Date(period.selectedStart)).startOf('day');
            end = (0, dayjs_1.default)(new Date(period.selectedEnd)).endOf('day');
            break;
        }
        case 'ENTIRE_LOAN': {
            const products = await LoanProducts_model_1.LoanProduct.find({ borrowerId: borrower._id.toString() }).lean();
            const productPeriods = await Promise.all(products.map(async (product) => {
                const periods = await (0, exports.getPeriods)(period.selectedPeriod, product._id.toString());
                const periodStart = periods[0].start.startOf('day');
                const periodEnd = periods[periods.length - 1].end.endOf('day');
                return { periodStart, periodEnd };
            }));
            const starts = productPeriods.map((p) => p.periodStart);
            const ends = productPeriods.map((p) => p.periodEnd);
            start = dayjs_1.default.min(starts);
            end = dayjs_1.default.max(ends);
            break;
        }
        default: {
            const periods = await (0, exports.getPeriods)(period.selectedPeriod, null);
            start = periods[0].start.startOf('day');
            end = periods[periods.length - 1].end.endOf('day');
            break;
        }
    }
    if (splitPeriods) {
        const result = [];
        let currentMonth = start.startOf('month');
        while (currentMonth.isBefore(end) || currentMonth.isSame(end, 'month')) {
            const startOfMonth = currentMonth.startOf('month');
            const endOfMonth = currentMonth.endOf('month');
            result.push({
                _id: borrower._id.toString(),
                code: borrower.code,
                name: borrower.name,
                period: {
                    selectedPeriod: period.selectedPeriod,
                    start: startOfMonth.utc().toDate().toISOString(),
                    end: endOfMonth.utc().toDate().toISOString(),
                },
            });
            currentMonth = currentMonth.add(1, 'month');
        }
        return result;
    }
    else {
        return [{
                _id: borrower._id.toString(),
                code: borrower.code,
                name: borrower.name,
                period: {
                    selectedPeriod: period.selectedPeriod,
                    start: start.utc().toDate().toISOString(),
                    end: end.utc().toDate().toISOString(),
                },
            }];
    }
};
exports.getBorrowerWithPeriods = getBorrowerWithPeriods;
const getPeriods = async (selectedPeriod, productId) => {
    let start;
    let end;
    switch (selectedPeriod) {
        case 'CURRENT_MONTH': {
            start = (0, dayjs_1.default)().utcOffset(0).startOf('month');
            end = (0, dayjs_1.default)().utcOffset(0).endOf('month');
            return [{ start, end }];
        }
        case 'LAST_MONTH': {
            start = (0, dayjs_1.default)().subtract(1, 'month').utcOffset(0).startOf('month');
            end = (0, dayjs_1.default)().subtract(1, 'month').utcOffset(0).endOf('month');
            return [{ start, end }];
        }
        case 'ENTIRE_LOAN': {
            const product = await (0, loan_products_db_1.getLoanProductById)(productId);
            const lastTransaction = await (0, loan_transactions_db_1.getLastTransactionForDate)(productId);
            start = (0, dayjs_1.default)(product.startDate).tz('UTC');
            let lastDate = (0, dayjs_1.default)(new Date()).endOf('month').toDate();
            if (lastTransaction) {
                if ((0, dayjs_1.default)(lastTransaction.date).tz('UTC').toDate() > (0, dayjs_1.default)(new Date()).endOf('month').toDate()) {
                    lastDate = lastTransaction.date;
                }
            }
            end = (0, dayjs_1.default)(lastDate);
            const finish = (0, dayjs_1.default)(lastDate);
            const periods = [];
            do {
                end = start.endOf('month');
                periods.push({ start, end });
                start = end.add(1, 'day').startOf('month').utc();
            } while (finish.toDate() > end.toDate());
            return periods;
        }
    }
    return [];
};
exports.getPeriods = getPeriods;
