"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SignsService = exports.ESignStatus = void 0;
const mongoose_1 = __importDefault(require("mongoose"));
const lodash_1 = __importDefault(require("lodash"));
const availability_db_1 = require("../db/availability.db");
const AvailabilitySigns_model_1 = require("../models/AvailabilitySigns.model");
const BBCDate_model_1 = require("../models/BBCDate.model");
const _models_1 = require("../models/_models");
const bbcDates_db_1 = require("../db/bbcDates.db");
const UserLog_model_1 = require("../models/UserLog.model");
const user_logs_db_1 = require("../db/user-logs.db");
const collaterals_db_1 = require("../db/collaterals.db");
const AvilabilitySignedData_model_1 = require("../models/AvilabilitySignedData.model");
var ESignStatus;
(function (ESignStatus) {
    ESignStatus["STARTED"] = "STARTED";
    ESignStatus["APPROVED"] = "APPROVED";
    ESignStatus["PENDING"] = "PENDING";
})(ESignStatus || (exports.ESignStatus = ESignStatus = {}));
class SignsService {
    lockService;
    loanTransactionsService;
    loanStatementService;
    usersService;
    constructor(lockService, loanTransactionsService, loanStatementService, usersService) {
        this.lockService = lockService;
        this.loanTransactionsService = loanTransactionsService;
        this.loanStatementService = loanStatementService;
        this.usersService = usersService;
    }
    async getAvailabilitiesSigns(bbcDateIds) {
        const availabilitySigns = await (0, availability_db_1.getAvailabilitySigns)(bbcDateIds);
        const users = await this.usersService.getUserList();
        const usersMap = users.reduce((acc, user) => {
            return { ...acc, [user.id]: user };
        }, {});
        return availabilitySigns.map((availabilitySign) => {
            return {
                ...availabilitySign,
                signs: availabilitySign.signs.map((sign) => ({
                    ...sign,
                    userSigned: usersMap[sign.userSignedId],
                    userRevoked: usersMap[sign.userRevokedId],
                })),
            };
        });
    }
    async getAvailabilitySigns(bbcDateIds) {
        const availabilitiesSigns = await this.getAvailabilitiesSigns(bbcDateIds);
        const availabilitiesSignsFull = bbcDateIds.map((bbcDateId) => {
            const foundAvailabilitySigns = availabilitiesSigns.find((signs) => signs.bbcDateId.toString() === bbcDateId);
            if (foundAvailabilitySigns) {
                return foundAvailabilitySigns;
            }
            return {
                _id: null,
                bbcDateId: new mongoose_1.default.Types.ObjectId(bbcDateId),
                requiredSigns: AvailabilitySigns_model_1.REQUIRED_SIGNS,
                signs: [],
            };
        });
        return availabilitiesSignsFull.map((availabilitySigns) => {
            const signs = availabilitySigns.signs.reduce((acc, userSign) => {
                if (!userSign.userSigned) {
                    return acc;
                }
                userSign.userSigned = (lodash_1.default.pick(userSign.userSigned, ['id', 'firstName', 'lastName']));
                if (userSign.userRevoked) {
                    userSign.userRevoked = (lodash_1.default.pick(userSign.userRevoked, ['id', 'firstName', 'lastName']));
                }
                return [...acc, lodash_1.default.pick(userSign, ['userSigned', 'signedAt', 'userRevoked', 'revokedAt'])];
            }, []);
            return { ...availabilitySigns, signs };
        });
    }
    async revokeAvailability(bbcDateId, userId, userIdRevoked) {
        const availability = await (0, availability_db_1.findAvailabilitySignsByBBCDateId)(bbcDateId);
        const updatedSigns = availability.signs.map((sign) => {
            if (sign.userSignedId.toString() === userId && !sign.revokedAt) {
                sign.userRevokedId = userIdRevoked;
                sign.revokedAt = new Date();
            }
            return sign;
        });
        await (0, availability_db_1.updateAvailabilitySigns)(bbcDateId, updatedSigns);
    }
    async signAvailability(bbcDateId, user) {
        const newSign = { userSignedId: user.id, signedAt: new Date() };
        return await (0, availability_db_1.addAvailabilitySign)(bbcDateId, newSign);
    }
    async isBBCLocked(bbcDateId) {
        const bbcDoc = await BBCDate_model_1.BBCDateModel.findById(bbcDateId).lean();
        if (!bbcDoc) {
            return false;
        }
        return await this.lockService.isDateLocked(bbcDoc.bbcDate, bbcDoc.borrowerId.toString());
    }
    async getLatestSignedBBCDate(borrowerId, date) {
        const bbcDocs = await (0, bbcDates_db_1.getOlderBBCDates)(borrowerId, date || new Date());
        const activeSignsAfter = await AvailabilitySigns_model_1.AvailabilitySignsModel.aggregate([
            {
                $match: {
                    'bbcDateId': { $in: bbcDocs.map((bbc) => bbc._id) },
                },
            }, {
                $unwind: {
                    path: '$signs',
                    preserveNullAndEmptyArrays: true,
                },
            }, {
                $match: {
                    'signs.revokedAt': { $exists: false },
                },
            }, {
                $group: {
                    '_id': '$_id',
                    'requiredSigns': { $first: '$requiredSigns' },
                    'bbcDateId': { $first: '$bbcDateId' },
                    'signs': { $push: '$signs' },
                },
            }, {
                $match: {
                    $expr: {
                        $gte: [
                            { '$size': '$signs' }, '$requiredSigns',
                        ],
                    },
                },
            }, {
                $lookup: {
                    from: _models_1.MODEL_NAMES.BBCDates,
                    localField: 'bbcDateId',
                    foreignField: '_id',
                    as: 'bbc',
                },
            }, {
                $unwind: {
                    path: '$bbc',
                    preserveNullAndEmptyArrays: true,
                },
            }, {
                $sort: {
                    'bbc.bbcDate': -1,
                },
            },
        ]);
        if (!activeSignsAfter.length) {
            return null;
        }
        return activeSignsAfter[0].bbc;
    }
    async getSavedData(bbcDateId) {
        const availabilitySignedData = await AvilabilitySignedData_model_1.AvailabilitySignedData.findOne({ bbcDateId }).lean();
        if (!availabilitySignedData) {
            return {};
        }
        return availabilitySignedData.data;
    }
    async keepBalance(bbcDateId) {
        const loanBalances = await this.loanTransactionsService.getLoanBalanceForBBCDateId(bbcDateId);
        const accruedStatement = await this.loanStatementService.calculateAccruedStatementForBBC(bbcDateId);
        console.log({ bbcDateId });
        await AvilabilitySignedData_model_1.AvailabilitySignedData.findOneAndUpdate({ bbcDateId }, {
            data: {
                loanBalances,
                accruedStatement,
            },
        }, { upsert: true });
    }
    async clearAvailabilityStoredData(bbcDateId) {
        await AvilabilitySignedData_model_1.AvailabilitySignedData.findOneAndDelete({ bbcDateId });
    }
    async logSignStatus(bbcDateId, userId, isSigned) {
        const bbcDate = await BBCDate_model_1.BBCDateModel.findById(bbcDateId)
            .populate('borrowerId')
            .lean();
        if (!bbcDate) {
            return;
        }
        const newLog = {
            userId,
            action: isSigned ? UserLog_model_1.ELogActionType.CREATE : UserLog_model_1.ELogActionType.DELETE,
            details: { date: bbcDate.bbcDate, borrower: bbcDate.borrowerId.name }, // , borrower: bbcDate.borrower.name
            logType: isSigned ? UserLog_model_1.ELogType.SIGN_BBC : UserLog_model_1.ELogType.REVOKE_BBC,
        };
        await (0, user_logs_db_1.createLog)(newLog);
    }
    async BBCDatesShort(borrowerId, paginatorOptions) {
        const BBCDates = await BBCDate_model_1.BBCDateModel.aggregate([
            {
                '$match': {
                    'borrowerId': new mongoose_1.default.Types.ObjectId(borrowerId),
                },
            }, {
                '$sort': {
                    'bbcDate': -1,
                },
            }, {
                '$project': {
                    '_id': 1,
                    'bbcDate': 1,
                    'signStatus': 1,
                },
            },
            ...(0, collaterals_db_1.ITEMS_PAGINATION)(paginatorOptions),
        ]);
        const availabilitiesSigns = await this.getAvailabilitiesSigns(BBCDates.map((bbcDate) => bbcDate._id.toString()));
        await Promise.all(BBCDates.slice().map(async (bbcDate) => {
            const foundAvailabilitySigns = availabilitiesSigns.find((aSigns) => aSigns.bbcDateId.toString() === bbcDate._id.toString());
            if (!foundAvailabilitySigns) {
                bbcDate.signStatus = ESignStatus.STARTED;
            }
            if (foundAvailabilitySigns) {
                const activeSigns = foundAvailabilitySigns.signs.filter((sign) => !sign.userRevoked).length;
                if (activeSigns === 0) {
                    bbcDate.signStatus = ESignStatus.STARTED;
                }
                else if (activeSigns === foundAvailabilitySigns.requiredSigns) {
                    bbcDate.signStatus = ESignStatus.APPROVED;
                }
                else if (activeSigns < foundAvailabilitySigns.requiredSigns) {
                    bbcDate.signStatus = ESignStatus.PENDING;
                }
            }
        }));
        return BBCDates;
    }
}
exports.SignsService = SignsService;
