"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.YieldService = void 0;
const lodash_1 = __importDefault(require("lodash"));
const mongoose_1 = __importDefault(require("mongoose"));
const date_helper_1 = require("../helpers/date.helper");
const Yield_model_1 = require("../models/Yield.model");
const LoanCharges_model_1 = require("../models/LoanCharges.model");
const LoanProducts_model_1 = require("../models/LoanProducts.model");
const microservice_tasks_db_1 = require("../db/microservice-tasks.db");
const microservice_task_enum_1 = require("../enums/microservice-task.enum");
class YieldService {
    async getCalculatedYieldData(charge, selectedMonth) {
        const oldData = await Yield_model_1.YieldData.findOne({
            chargeId: charge._id,
            productId: charge.productId,
            year: selectedMonth.year,
            month: selectedMonth.month,
        }).lean();
        if (oldData) {
            return oldData.valuePercent;
        }
        return null;
    }
    async getCalculatedYieldTotals(productId, selectedMonth) {
        return Yield_model_1.YieldData.find({
            chargeId: null,
            productId: productId,
            year: selectedMonth.year,
            month: selectedMonth.month,
        }).lean();
    }
    async getCalculatedYieldTotalsForPeriod(productIds, periodStart, periodEnd) {
        return Yield_model_1.YieldData.find({
            productId: { $in: productIds.map((productId) => new mongoose_1.default.Types.ObjectId(productId)) },
            $or: [
                {
                    year: periodStart.year,
                    month: { $gte: periodStart.month },
                },
                {
                    year: periodEnd.year,
                    month: { $lte: periodEnd.month },
                },
                {
                    year: { $gt: periodStart.year, $lt: periodEnd.year },
                },
            ],
        }).sort({
            year: 1,
            month: 1,
        }).lean();
    }
    async getYieldData(params) {
        const validationRes = Yield_model_1.yieldParamsValidationSchema.validate(params);
        if (validationRes.error) {
            console.error(validationRes.error);
            return null;
        }
        if (params.ignoreOldData) {
            return await this.getEmptyResult(params);
        }
        const startDate = (0, date_helper_1.convertSelectedMonthToDate)(params.periodStart);
        const endDate = (0, date_helper_1.convertSelectedMonthToDate)(params.periodEnd);
        const charges = await LoanCharges_model_1.LoanCharge.find({ includeInYield: true }).lean();
        const productIDs = charges.reduce((acc, charge) => [...acc, charge.productId.toString()], []);
        const products = await LoanProducts_model_1.LoanProduct
            .find({ _id: { $in: Object.values(productIDs).map((id) => new mongoose_1.default.Types.ObjectId(id)) } })
            .populate('borrowerId')
            .lean();
        const productMap = products
            .reduce((acc, product) => {
            return {
                ...acc,
                [product._id.toString()]: {
                    product,
                    charges: charges.filter((charge) => charge.productId.toString() == product._id.toString()),
                },
            };
        }, {});
        const allDates = (0, date_helper_1.getStartOfMonthsBetweenDates)(startDate, endDate);
        const allResults = [];
        let hasAllData = true;
        await Promise.all(Object.keys(productMap).map(async (productId) => {
            await Promise.all(productMap[productId].charges.map(async (charge) => {
                const monthlyData = {
                    charge,
                    product: productMap[charge.productId.toString()].product,
                    order: 0,
                    title: productMap[charge.productId.toString()].product.name,
                };
                await Promise.all(Object.entries(allDates).map(async ([dateView, date]) => {
                    if (!hasAllData) {
                        return;
                    }
                    const yieldDataValuePercent = await this.getCalculatedYieldData(charge, date);
                    if (!yieldDataValuePercent) {
                        console.error(`Yield data not found for product ${productMap[productId].product.name} for charge ${charge.name} and date ${dateView}`);
                        hasAllData = false;
                    }
                    else {
                        monthlyData[`valuePercent-${dateView}`] = yieldDataValuePercent;
                    }
                }));
                allResults.push({ ...monthlyData });
            }));
        }));
        if (!hasAllData) {
            // return await this.getEmptyResult(params); // TODO return it
        }
        await Promise.all(products.map(async (product) => {
            const productTotalList = [
                Yield_model_1.ETotalType.GROSS,
                Yield_model_1.ETotalType.BROKER,
                Yield_model_1.ETotalType.NET,
                Yield_model_1.ETotalType.AVERAGE_BALANCE,
                Yield_model_1.ETotalType.AVERAGE_LIFE_BALANCE,
                Yield_model_1.ETotalType.APR_GROSS,
                Yield_model_1.ETotalType.APR_LIFE_GROSS,
            ];
            const productTotal = productTotalList.reduce((acc, p) => {
                return {
                    ...acc,
                    [p]: {
                        charge: null,
                        product,
                        order: Yield_model_1.YIELD_TOTALS_MAP[p].order,
                        title: `${product.name} (${Yield_model_1.YIELD_TOTALS_MAP[p].title})`,
                        class: Yield_model_1.YIELD_TOTALS_MAP[p].class,
                    },
                };
            }, {});
            await Promise.all(Object.entries(allDates).map(async ([dateView, dateValue]) => {
                const productTotals = await this.getCalculatedYieldTotals(product._id.toString(), dateValue);
                Object.keys(productTotal).forEach((totalType) => {
                    const foundType = productTotals.find((total) => total.totalType === totalType);
                    if (foundType) {
                        productTotal[totalType][`value-${dateView}`] = foundType.value;
                        productTotal[totalType][`valuePercent-${dateView}`] = foundType.valuePercent;
                    }
                });
            }));
            Object.values(productTotal).forEach((values) => {
                allResults.push(values);
            });
        }));
        const sortedData = lodash_1.default.orderBy(allResults, ['product.borrowerId.name', 'product.order', 'order', 'charge.name'], ['asc', 'asc', 'asc', 'asc']);
        return { data: sortedData, dateColumns: Object.keys(allDates) };
    }
    async createYieldTask(params) {
        await (0, microservice_tasks_db_1.createMicroserviceTasks)(microservice_task_enum_1.EMicroserviceTask.YIELD, params);
    }
    async getEmptyResult(params) {
        await this.createYieldTask(params);
        return { data: [], dateColumns: [] };
    }
}
exports.YieldService = YieldService;
