var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import DatabaseService from "./DatabaseService";
import OnCallDutyPolicyScheduleLayerService from "./OnCallDutyPolicyScheduleLayerService";
import OnCallDutyPolicyScheduleLayerUserService from "./OnCallDutyPolicyScheduleLayerUserService";
import OnCallDutyPolicyUserOverrideService from "./OnCallDutyPolicyUserOverrideService";
import SortOrder from "../../Types/BaseDatabase/SortOrder";
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
import OneUptimeDate from "../../Types/Date";
import ObjectID from "../../Types/ObjectID";
import LayerUtil from "../../Types/OnCallDutyPolicy/Layer";
import UserOverrideUtil from "../../Types/OnCallDutyPolicy/UserOverrideUtil";
import QueryHelper from "../Types/Database/QueryHelper";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
import OnCallDutyPolicySchedule from "../../Models/DatabaseModels/OnCallDutyPolicySchedule";
import OnCallDutyPolicyEscalationRuleScheduleService from "./OnCallDutyPolicyEscalationRuleScheduleService";
import EmailTemplateType from "../../Types/Email/EmailTemplateType";
import UserService from "./UserService";
import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
import UserNotificationSettingService from "./UserNotificationSettingService";
import NotificationSettingEventType from "../../Types/NotificationSetting/NotificationSettingEventType";
import BadDataException from "../../Types/Exception/BadDataException";
import Timezone from "../../Types/Timezone";
import logger from "../Utils/Logger";
import OnCallDutyPolicyFeedService from "./OnCallDutyPolicyFeedService";
import { OnCallDutyPolicyFeedEventType } from "../../Models/DatabaseModels/OnCallDutyPolicyFeed";
import { Green500 } from "../../Types/BrandColors";
import OnCallDutyPolicyTimeLogService from "./OnCallDutyPolicyTimeLogService";
import OnCallDutyPolicyScheduleLabelRuleEngineService from "./OnCallDutyPolicyScheduleLabelRuleEngineService";
import OnCallDutyPolicyScheduleOwnerRuleEngineService from "./OnCallDutyPolicyScheduleOwnerRuleEngineService";
import PushNotificationUtil from "../Utils/PushNotificationUtil";
import { createWhatsAppMessageFromTemplate } from "../Utils/WhatsAppTemplateUtil";
export class Service extends DatabaseService {
    constructor() {
        super(OnCallDutyPolicySchedule);
        this.layerUtil = new LayerUtil();
    }
    async onCreateSuccess(_onCreate, createdItem) {
        if (createdItem.projectId && createdItem.id) {
            Promise.resolve()
                .then(async () => {
                await OnCallDutyPolicyScheduleLabelRuleEngineService.applyRulesToSchedule(createdItem);
            })
                .then(async () => {
                await OnCallDutyPolicyScheduleOwnerRuleEngineService.applyRulesToSchedule(createdItem);
            })
                .catch((error) => {
                var _a, _b;
                logger.error(`Error applying on-call schedule rules in OnCallDutyPolicyScheduleService.onCreateSuccess: ${error}`, {
                    projectId: (_a = createdItem.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    onCallDutyPolicyScheduleId: (_b = createdItem.id) === null || _b === void 0 ? void 0 : _b.toString(),
                });
            });
        }
        return createdItem;
    }
    async onBeforeDelete(deleteBy) {
        const callSchedules = await this.findBy({
            query: deleteBy.query,
            select: {
                _id: true,
                projectId: true,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
            props: {
                isRoot: true,
            },
        });
        for (const schedule of callSchedules) {
            OnCallDutyPolicyTimeLogService.endTimeForSchedule({
                projectId: schedule.projectId,
                onCallDutyPolicyScheduleId: schedule.id,
                endsAt: OneUptimeDate.getCurrentDate(),
            }).catch((err) => {
                var _a;
                logger.error(err, {
                    projectId: (_a = schedule.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                });
            });
        }
        return {
            deleteBy: deleteBy,
            carryForward: {},
        };
    }
    async getOnCallSchedulesWhereUserIsOnCallDuty(data) {
        const schedules = await this.findBy({
            query: {
                projectId: data.projectId,
                currentUserIdOnRoster: data.userId,
            },
            select: {
                _id: true,
                name: true,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
            props: {
                isRoot: true,
            },
        });
        return schedules;
    }
    async sendNotificationToUserOnScheduleHandoff(data) {
        // Before we send any notification, we need to check if this schedule is attached to any on-call policy.
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
        const escalationRulesAttachedToSchedule = await OnCallDutyPolicyEscalationRuleScheduleService.findBy({
            query: {
                onCallDutyPolicyScheduleId: data.scheduleId,
            },
            select: {
                projectId: true,
                _id: true,
                onCallDutyPolicy: {
                    name: true,
                    _id: true,
                },
                onCallDutyPolicyEscalationRule: {
                    name: true,
                    _id: true,
                    order: true,
                },
                onCallDutyPolicySchedule: {
                    name: true,
                    _id: true,
                },
            },
            props: {
                isRoot: true,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
        });
        if (escalationRulesAttachedToSchedule.length === 0) {
            // do nothing.
            return;
        }
        for (const escalationRule of escalationRulesAttachedToSchedule) {
            const projectId = escalationRule.projectId;
            const onCallSchedule = escalationRule.onCallDutyPolicySchedule;
            if (!onCallSchedule) {
                continue;
            }
            const onCallPolicy = escalationRule.onCallDutyPolicy;
            if (!onCallPolicy) {
                continue;
            }
            const onCallDutyPolicyEscalationRule = escalationRule.onCallDutyPolicyEscalationRule;
            if (!onCallDutyPolicyEscalationRule) {
                continue;
            }
            const { previousInformation, newInformation } = data;
            /*
             * if there's a change, witht he current user, send notification to the new current user.
             * Send notificiation to the new current user.
             */
            if (((_a = previousInformation.currentUserIdOnRoster) === null || _a === void 0 ? void 0 : _a.toString()) !==
                ((_b = newInformation.currentUserIdOnRoster) === null || _b === void 0 ? void 0 : _b.toString()) ||
                ((_c = previousInformation.rosterHandoffAt) === null || _c === void 0 ? void 0 : _c.toString()) !==
                    ((_d = newInformation.rosterHandoffAt) === null || _d === void 0 ? void 0 : _d.toString())) {
                if (((_e = previousInformation.currentUserIdOnRoster) === null || _e === void 0 ? void 0 : _e.toString()) !==
                    ((_f = newInformation.currentUserIdOnRoster) === null || _f === void 0 ? void 0 : _f.toString()) &&
                    ((_g = previousInformation.currentUserIdOnRoster) === null || _g === void 0 ? void 0 : _g.toString())) {
                    // the user has changed. Send notifiction to old user that he has been removed.
                    // send notification to the new current user.
                    const sendEmailToUserId = previousInformation.currentUserIdOnRoster;
                    const userTimezone = await UserService.getTimezoneForUser(sendEmailToUserId);
                    const vars = {
                        onCallPolicyName: onCallPolicy.name || "No name provided",
                        escalationRuleName: onCallDutyPolicyEscalationRule.name || "No name provided",
                        escalationRuleOrder: ((_h = onCallDutyPolicyEscalationRule.order) === null || _h === void 0 ? void 0 : _h.toString()) || "-",
                        reason: "Your on-call roster on schedule " +
                            onCallSchedule.name +
                            " just ended.",
                        rosterStartsAt: OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
                            date: previousInformation.rosterStartAt,
                            timezones: userTimezone ? [userTimezone] : [Timezone.GMT],
                        }),
                        rosterEndsAt: OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
                            date: OneUptimeDate.isInTheFuture(previousInformation.rosterHandoffAt)
                                ? OneUptimeDate.getCurrentDate()
                                : previousInformation.rosterHandoffAt,
                            timezones: userTimezone ? [userTimezone] : [Timezone.GMT],
                        }),
                        onCallPolicyViewLink: (await OnCallDutyPolicyService.getOnCallDutyPolicyLinkInDashboard(projectId, onCallPolicy.id)).toString(),
                    };
                    // current user changed, send alert the new current user.
                    const emailMessage = {
                        templateType: EmailTemplateType.UserNoLongerActiveOnOnCallRoster,
                        vars: vars,
                        subject: "You are no longer on-call for " + onCallPolicy.name,
                    };
                    const sms = {
                        message: `This is a message from OneUptime. You are no longer on-call for ${onCallPolicy.name} because your on-call roster on schedule ${onCallSchedule.name} just ended. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.`,
                    };
                    const callMessage = {
                        data: [
                            {
                                sayMessage: `This is a message from OneUptime. You are no longer on-call for ${onCallPolicy.name} because your on-call roster on schedule ${onCallSchedule.name} just ended. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.  Good bye.`,
                            },
                        ],
                    };
                    const pushMessage = PushNotificationUtil.createGenericNotification({
                        title: "On-Call Duty Ended",
                        body: `You are no longer on-call for ${onCallPolicy.name} as your roster on schedule ${onCallSchedule.name} has ended.`,
                        tag: "on-call-duty-ended",
                        requireInteraction: false,
                    });
                    const eventType = NotificationSettingEventType.SEND_WHEN_USER_IS_NO_LONGER_ACTIVE_ON_ON_CALL_ROSTER;
                    const whatsAppMessage = createWhatsAppMessageFromTemplate({
                        eventType,
                        templateVariables: {
                            on_call_policy_name: onCallPolicy.name,
                            schedule_name: onCallSchedule.name,
                            schedule_link: vars["onCallPolicyViewLink"] || "",
                        },
                    });
                    await UserNotificationSettingService.sendUserNotification({
                        userId: sendEmailToUserId,
                        projectId: projectId,
                        emailEnvelope: emailMessage,
                        smsMessage: sms,
                        callRequestMessage: callMessage,
                        pushNotificationMessage: pushMessage,
                        whatsAppMessage,
                        eventType,
                        onCallPolicyId: escalationRule.onCallDutyPolicy.id,
                        onCallPolicyEscalationRuleId: escalationRule.onCallDutyPolicyEscalationRule.id,
                        onCallScheduleId: data.scheduleId,
                    });
                    // add end log for user.
                    OnCallDutyPolicyTimeLogService.endTimeLogForUser({
                        userId: sendEmailToUserId,
                        onCallDutyPolicyScheduleId: data.scheduleId,
                        onCallDutyPolicyEscalationRuleId: escalationRule.onCallDutyPolicyEscalationRule.id,
                        onCallDutyPolicyId: escalationRule.onCallDutyPolicy.id,
                        projectId: projectId,
                        endsAt: OneUptimeDate.getCurrentDate(),
                    }).catch((err) => {
                        logger.error("Error ending time log for user: " +
                            sendEmailToUserId.toString() +
                            " for schedule: " +
                            data.scheduleId.toString(), {
                            projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
                            userId: sendEmailToUserId === null || sendEmailToUserId === void 0 ? void 0 : sendEmailToUserId.toString(),
                        });
                        logger.error(err, {
                            projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
                            userId: sendEmailToUserId === null || sendEmailToUserId === void 0 ? void 0 : sendEmailToUserId.toString(),
                        });
                    });
                    const onCallDutyPolicyId = escalationRule.onCallDutyPolicy.id;
                    // Send workspace notifiction as well.
                    await OnCallDutyPolicyFeedService.createOnCallDutyPolicyFeedItem({
                        onCallDutyPolicyId: onCallDutyPolicyId,
                        projectId: projectId,
                        onCallDutyPolicyFeedEventType: OnCallDutyPolicyFeedEventType.RosterHandoff,
                        displayColor: Green500,
                        feedInfoInMarkdown: `🚫 **${await UserService.getUserMarkdownString({
                            userId: sendEmailToUserId,
                            projectId: projectId,
                        })}** is no longer on call for [On-Call Policy ${(_j = escalationRule.onCallDutyPolicy) === null || _j === void 0 ? void 0 : _j.name}](${(await OnCallDutyPolicyService.getOnCallDutyPolicyLinkInDashboard(projectId, onCallDutyPolicyId)).toString()}) escalation rule **${(_k = escalationRule.onCallDutyPolicyEscalationRule) === null || _k === void 0 ? void 0 : _k.name}** with order **${(_l = escalationRule.onCallDutyPolicyEscalationRule) === null || _l === void 0 ? void 0 : _l.order}** because your on-call roster on schedule **${onCallSchedule.name}** just ended.`,
                        userId: sendEmailToUserId || undefined,
                        workspaceNotification: {
                            sendWorkspaceNotification: true,
                            notifyUserId: undefined,
                        },
                    });
                }
                if ((_m = newInformation.currentUserIdOnRoster) === null || _m === void 0 ? void 0 : _m.toString()) {
                    // send email to the new current user.
                    const sendEmailToUserId = newInformation.currentUserIdOnRoster;
                    const userTimezone = await UserService.getTimezoneForUser(sendEmailToUserId);
                    const vars = {
                        onCallPolicyName: onCallPolicy.name || "No name provided",
                        escalationRuleName: onCallDutyPolicyEscalationRule.name || "No name provided",
                        escalationRuleOrder: ((_o = onCallDutyPolicyEscalationRule.order) === null || _o === void 0 ? void 0 : _o.toString()) || "-",
                        reason: "You are now on-call for the policy " +
                            onCallPolicy.name +
                            " because your on-call roster on schedule " +
                            onCallSchedule.name,
                        rosterStartsAt: OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
                            date: newInformation.rosterStartAt,
                            timezones: userTimezone ? [userTimezone] : [Timezone.GMT],
                        }),
                        rosterEndsAt: OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
                            date: newInformation.rosterHandoffAt,
                            timezones: userTimezone ? [userTimezone] : [Timezone.GMT],
                        }),
                        onCallPolicyViewLink: (await OnCallDutyPolicyService.getOnCallDutyPolicyLinkInDashboard(projectId, onCallPolicy.id)).toString(),
                    };
                    const emailMessage = {
                        templateType: EmailTemplateType.UserCurrentlyOnOnCallRoster,
                        vars: vars,
                        subject: "You are now on-call for " + onCallPolicy.name,
                    };
                    const sms = {
                        message: `This is a message from OneUptime. You are now on-call for ${onCallPolicy.name} because you are now on the roster for schedule ${onCallSchedule.name}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.`,
                    };
                    const callMessage = {
                        data: [
                            {
                                sayMessage: `This is a message from OneUptime. You are now on-call for ${onCallPolicy.name} because you are now on the roster for schedule ${onCallSchedule.name}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.  Good bye.`,
                            },
                        ],
                    };
                    const pushMessage = PushNotificationUtil.createGenericNotification({
                        title: "On-Call Duty Started",
                        body: `You are now on-call for ${onCallPolicy.name} on schedule ${onCallSchedule.name}.`,
                        tag: "on-call-duty-started",
                        requireInteraction: true,
                    });
                    const eventType = NotificationSettingEventType.SEND_WHEN_USER_IS_ON_CALL_ROSTER;
                    const whatsAppMessage = createWhatsAppMessageFromTemplate({
                        eventType,
                        templateVariables: {
                            on_call_policy_name: onCallPolicy.name,
                            schedule_name: onCallSchedule.name,
                            schedule_link: vars["onCallPolicyViewLink"] || "",
                        },
                    });
                    await UserNotificationSettingService.sendUserNotification({
                        userId: sendEmailToUserId,
                        projectId: projectId,
                        emailEnvelope: emailMessage,
                        smsMessage: sms,
                        callRequestMessage: callMessage,
                        pushNotificationMessage: pushMessage,
                        whatsAppMessage,
                        eventType,
                        onCallPolicyId: escalationRule.onCallDutyPolicy.id,
                        onCallPolicyEscalationRuleId: escalationRule.onCallDutyPolicyEscalationRule.id,
                        onCallScheduleId: data.scheduleId,
                    });
                    // add start log for user.
                    OnCallDutyPolicyTimeLogService.startTimeLogForUser({
                        userId: sendEmailToUserId,
                        onCallDutyPolicyScheduleId: data.scheduleId,
                        onCallDutyPolicyEscalationRuleId: escalationRule.onCallDutyPolicyEscalationRule.id,
                        onCallDutyPolicyId: escalationRule.onCallDutyPolicy.id,
                        projectId: projectId,
                        startsAt: OneUptimeDate.getCurrentDate(),
                    }).catch((err) => {
                        logger.error("Error starting time log for user: " +
                            sendEmailToUserId.toString() +
                            " for schedule: " +
                            data.scheduleId.toString(), {
                            projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
                            userId: sendEmailToUserId === null || sendEmailToUserId === void 0 ? void 0 : sendEmailToUserId.toString(),
                        });
                        logger.error(err, {
                            projectId: projectId === null || projectId === void 0 ? void 0 : projectId.toString(),
                            userId: sendEmailToUserId === null || sendEmailToUserId === void 0 ? void 0 : sendEmailToUserId.toString(),
                        });
                    });
                    const onCallDutyPolicyId = escalationRule.onCallDutyPolicy.id;
                    // Send workspace notifiction as well.
                    await OnCallDutyPolicyFeedService.createOnCallDutyPolicyFeedItem({
                        onCallDutyPolicyId: onCallDutyPolicyId,
                        projectId: projectId,
                        onCallDutyPolicyFeedEventType: OnCallDutyPolicyFeedEventType.RosterHandoff,
                        displayColor: Green500,
                        feedInfoInMarkdown: `📞 **${await UserService.getUserMarkdownString({
                            userId: sendEmailToUserId,
                            projectId: projectId,
                        })}** is currently on call for [On-Call Policy ${(_p = escalationRule.onCallDutyPolicy) === null || _p === void 0 ? void 0 : _p.name}](${(await OnCallDutyPolicyService.getOnCallDutyPolicyLinkInDashboard(projectId, onCallDutyPolicyId)).toString()}) escalation rule **${(_q = escalationRule.onCallDutyPolicyEscalationRule) === null || _q === void 0 ? void 0 : _q.name}** with order **${(_r = escalationRule.onCallDutyPolicyEscalationRule) === null || _r === void 0 ? void 0 : _r.order}** because of schedule **${onCallSchedule.name}** and your on-call roster starts at **${OneUptimeDate.getDateAsFormattedStringInMultipleTimezones({
                            date: newInformation.rosterStartAt,
                            timezones: userTimezone ? [userTimezone] : [Timezone.GMT],
                        })}** and ends at **${OneUptimeDate.getDateAsFormattedStringInMultipleTimezones({
                            date: newInformation.rosterHandoffAt,
                            timezones: userTimezone ? [userTimezone] : [Timezone.GMT],
                        })}**.`,
                        userId: sendEmailToUserId || undefined,
                        workspaceNotification: {
                            sendWorkspaceNotification: true,
                            notifyUserId: undefined,
                        },
                    });
                }
            }
            // send an email to the next user.
            if (((_s = previousInformation.nextUserIdOnRoster) === null || _s === void 0 ? void 0 : _s.toString()) !==
                ((_t = newInformation.nextUserIdOnRoster) === null || _t === void 0 ? void 0 : _t.toString()) ||
                ((_u = previousInformation.nextHandOffTimeAt) === null || _u === void 0 ? void 0 : _u.toString()) !==
                    ((_v = newInformation.nextHandOffTimeAt) === null || _v === void 0 ? void 0 : _v.toString()) ||
                ((_w = previousInformation.nextRosterStartAt) === null || _w === void 0 ? void 0 : _w.toString()) !==
                    ((_x = newInformation.nextRosterStartAt) === null || _x === void 0 ? void 0 : _x.toString())) {
                if ((_y = newInformation.nextUserIdOnRoster) === null || _y === void 0 ? void 0 : _y.toString()) {
                    // send email to the next user.
                    const sendEmailToUserId = newInformation.nextUserIdOnRoster;
                    const userTimezone = await UserService.getTimezoneForUser(sendEmailToUserId);
                    const vars = {
                        onCallPolicyName: onCallPolicy.name || "No name provided",
                        escalationRuleName: onCallDutyPolicyEscalationRule.name || "No name provided",
                        escalationRuleOrder: ((_z = onCallDutyPolicyEscalationRule.order) === null || _z === void 0 ? void 0 : _z.toString()) || "-",
                        reason: "You are next on-call for the policy " +
                            onCallPolicy.name +
                            " because your on-call roster on schedule " +
                            onCallSchedule.name +
                            " will start when the next handoff happens.",
                        rosterStartsAt: OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
                            date: newInformation.nextRosterStartAt,
                            timezones: userTimezone ? [userTimezone] : [Timezone.GMT],
                        }),
                        rosterEndsAt: OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
                            date: newInformation.nextHandOffTimeAt,
                            timezones: userTimezone ? [userTimezone] : [Timezone.GMT],
                        }),
                        onCallPolicyViewLink: (await OnCallDutyPolicyService.getOnCallDutyPolicyLinkInDashboard(projectId, onCallPolicy.id)).toString(),
                    };
                    const emailMessage = {
                        templateType: EmailTemplateType.UserNextOnOnCallRoster,
                        vars: vars,
                        subject: "You are next on-call for " + onCallPolicy.name,
                    };
                    const sms = {
                        message: `This is a message from OneUptime. You are next on-call for ${onCallPolicy.name} because your on-call roster on schedule ${onCallSchedule.name} will start when the next handoff happens. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.`,
                    };
                    const callMessage = {
                        data: [
                            {
                                sayMessage: `This is a message from OneUptime. You are next on-call for ${onCallPolicy.name} because your on-call roster on schedule ${onCallSchedule.name} will start when the next handoff happens. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.  Good bye.`,
                            },
                        ],
                    };
                    const pushMessage = PushNotificationUtil.createGenericNotification({
                        title: "Next On-Call Duty",
                        body: `You are next on-call for ${onCallPolicy.name} on schedule ${onCallSchedule.name}.`,
                        tag: "next-on-call-duty",
                        requireInteraction: false,
                    });
                    const eventType = NotificationSettingEventType.SEND_WHEN_USER_IS_NEXT_ON_CALL_ROSTER;
                    const whatsAppMessage = createWhatsAppMessageFromTemplate({
                        eventType,
                        templateVariables: {
                            on_call_policy_name: onCallPolicy.name,
                            schedule_name: onCallSchedule.name,
                            schedule_link: vars["onCallPolicyViewLink"] || "",
                        },
                    });
                    await UserNotificationSettingService.sendUserNotification({
                        userId: sendEmailToUserId,
                        projectId: projectId,
                        emailEnvelope: emailMessage,
                        smsMessage: sms,
                        callRequestMessage: callMessage,
                        pushNotificationMessage: pushMessage,
                        whatsAppMessage,
                        eventType,
                        onCallPolicyId: escalationRule.onCallDutyPolicy.id,
                        onCallPolicyEscalationRuleId: escalationRule.onCallDutyPolicyEscalationRule.id,
                        onCallScheduleId: data.scheduleId,
                    });
                    const onCallDutyPolicyId = escalationRule.onCallDutyPolicy.id;
                    // Send workspace notifiction as well.
                    await OnCallDutyPolicyFeedService.createOnCallDutyPolicyFeedItem({
                        onCallDutyPolicyId: onCallDutyPolicyId,
                        projectId: projectId,
                        onCallDutyPolicyFeedEventType: OnCallDutyPolicyFeedEventType.RosterHandoff,
                        displayColor: Green500,
                        feedInfoInMarkdown: `➡️ **${await UserService.getUserMarkdownString({
                            userId: sendEmailToUserId,
                            projectId: projectId,
                        })}** is next on call for [On-Call Policy ${(_0 = escalationRule.onCallDutyPolicy) === null || _0 === void 0 ? void 0 : _0.name}](${(await OnCallDutyPolicyService.getOnCallDutyPolicyLinkInDashboard(projectId, onCallDutyPolicyId)).toString()}) escalation rule **${(_1 = escalationRule.onCallDutyPolicyEscalationRule) === null || _1 === void 0 ? void 0 : _1.name}** with order **${(_2 = escalationRule.onCallDutyPolicyEscalationRule) === null || _2 === void 0 ? void 0 : _2.order}**. The on-call roster on schedule **${onCallSchedule.name}** will start when the next handoff happens which is at **${OneUptimeDate.getDateAsFormattedStringInMultipleTimezones({
                            date: newInformation.nextRosterStartAt,
                            timezones: userTimezone ? [userTimezone] : [Timezone.GMT],
                        })}** and will end at **${OneUptimeDate.getDateAsFormattedStringInMultipleTimezones({
                            date: newInformation.nextHandOffTimeAt,
                            timezones: userTimezone ? [userTimezone] : [Timezone.GMT],
                        })}**.`,
                        userId: sendEmailToUserId || undefined,
                        workspaceNotification: {
                            sendWorkspaceNotification: true,
                            notifyUserId: undefined,
                        },
                    });
                }
            }
        }
    }
    async refreshCurrentUserIdAndHandoffTimeInSchedule(scheduleId) {
        logger.debug("refreshCurrentUserIdAndHandoffTimeInSchedule called with scheduleId: " +
            scheduleId.toString(), { onCallDutyPolicyScheduleId: scheduleId.toString() });
        // get previoius result.
        logger.debug("Fetching previous schedule information for scheduleId: " +
            scheduleId.toString(), { onCallDutyPolicyScheduleId: scheduleId.toString() });
        const onCallSchedule = await this.findOneById({
            id: scheduleId,
            select: {
                currentUserIdOnRoster: true,
                rosterHandoffAt: true,
                nextUserIdOnRoster: true,
                rosterNextHandoffAt: true,
                rosterStartAt: true,
                rosterNextStartAt: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!onCallSchedule) {
            logger.debug("Schedule not found for scheduleId: " + scheduleId.toString(), { onCallDutyPolicyScheduleId: scheduleId.toString() });
            throw new BadDataException("Schedule not found");
        }
        logger.debug("Previous schedule information fetched for scheduleId: " +
            scheduleId.toString(), { onCallDutyPolicyScheduleId: scheduleId.toString() });
        const previousInformation = {
            currentUserIdOnRoster: onCallSchedule.currentUserIdOnRoster || null,
            rosterHandoffAt: onCallSchedule.rosterHandoffAt || null,
            nextUserIdOnRoster: onCallSchedule.nextUserIdOnRoster || null,
            nextHandOffTimeAt: onCallSchedule.rosterNextHandoffAt || null,
            rosterStartAt: onCallSchedule.rosterStartAt || null,
            nextRosterStartAt: onCallSchedule.rosterNextStartAt || null,
        };
        logger.debug(previousInformation, {
            onCallDutyPolicyScheduleId: scheduleId.toString(),
        });
        logger.debug("Fetching new schedule information for scheduleId: " +
            scheduleId.toString(), { onCallDutyPolicyScheduleId: scheduleId.toString() });
        const newInformation = await this.getCurrrentUserIdAndHandoffTimeInSchedule(scheduleId);
        logger.debug(newInformation, {
            onCallDutyPolicyScheduleId: scheduleId.toString(),
        });
        logger.debug("Updating schedule with new information for scheduleId: " +
            scheduleId.toString(), { onCallDutyPolicyScheduleId: scheduleId.toString() });
        await this.updateOneById({
            id: scheduleId,
            data: {
                currentUserIdOnRoster: newInformation.currentUserId,
                rosterHandoffAt: newInformation.handOffTimeAt,
                nextUserIdOnRoster: newInformation.nextUserId,
                rosterNextHandoffAt: newInformation.nextHandOffTimeAt,
                rosterStartAt: newInformation.rosterStartAt,
                rosterNextStartAt: newInformation.nextRosterStartAt,
            },
            props: {
                isRoot: true,
                ignoreHooks: true,
            },
        });
        logger.debug("Sending notifications for schedule handoff for scheduleId: " +
            scheduleId.toString(), { onCallDutyPolicyScheduleId: scheduleId.toString() });
        // send notification to the users.
        await this.sendNotificationToUserOnScheduleHandoff({
            scheduleId: scheduleId,
            previousInformation: previousInformation,
            newInformation: {
                currentUserIdOnRoster: newInformation.currentUserId,
                rosterHandoffAt: newInformation.handOffTimeAt,
                nextUserIdOnRoster: newInformation.nextUserId,
                nextHandOffTimeAt: newInformation.nextHandOffTimeAt,
                rosterStartAt: newInformation.rosterStartAt,
                nextRosterStartAt: newInformation.nextRosterStartAt,
            },
        });
        logger.debug("Returning new schedule information for scheduleId: " +
            scheduleId.toString(), { onCallDutyPolicyScheduleId: scheduleId.toString() });
        return newInformation;
    }
    async getCurrrentUserIdAndHandoffTimeInSchedule(scheduleId) {
        logger.debug("getCurrrentUserIdAndHandoffTimeInSchedule called with scheduleId: " +
            scheduleId.toString(), { onCallDutyPolicyScheduleId: scheduleId.toString() });
        const resultReturn = {
            currentUserId: null,
            handOffTimeAt: null,
            nextUserId: null,
            nextHandOffTimeAt: null,
            rosterStartAt: null,
            nextRosterStartAt: null,
        };
        logger.debug("Fetching events for scheduleId: " + scheduleId.toString(), {
            onCallDutyPolicyScheduleId: scheduleId.toString(),
        });
        const events = await this.getEventByIndexInSchedule({
            scheduleId: scheduleId,
            getNumberOfEvents: 2,
        });
        logger.debug("Events fetched: " + JSON.stringify(events), {
            onCallDutyPolicyScheduleId: scheduleId.toString(),
        });
        let currentEvent = events[0] || null;
        let nextEvent = events[1] || null;
        logger.debug("Current event: " + JSON.stringify(currentEvent), {
            onCallDutyPolicyScheduleId: scheduleId.toString(),
        });
        logger.debug("Next event: " + JSON.stringify(nextEvent), {
            onCallDutyPolicyScheduleId: scheduleId.toString(),
        });
        // if the current event start time in the future then the current event is the next event.
        if (currentEvent && OneUptimeDate.isInTheFuture(currentEvent.start)) {
            logger.debug("Current event is in the future, treating it as next event.", { onCallDutyPolicyScheduleId: scheduleId.toString() });
            nextEvent = currentEvent;
            currentEvent = null;
        }
        if (currentEvent) {
            logger.debug("Processing current event: " + JSON.stringify(currentEvent), { onCallDutyPolicyScheduleId: scheduleId.toString() });
            const userId = currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent.title; // this is user id in string.
            if (userId) {
                logger.debug("Current userId: " + userId, {
                    onCallDutyPolicyScheduleId: scheduleId.toString(),
                });
                resultReturn.currentUserId = new ObjectID(userId);
            }
            // get handOffTime
            const handOffTime = currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent.end; // this is user id in string.
            if (handOffTime) {
                logger.debug("Current handOffTime: " + handOffTime.toISOString(), {
                    onCallDutyPolicyScheduleId: scheduleId.toString(),
                });
                resultReturn.handOffTimeAt = handOffTime;
            }
            // get start time
            const startTime = currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent.start; // this is user id in string.
            if (startTime) {
                logger.debug("Current rosterStartAt: " + startTime.toISOString(), {
                    onCallDutyPolicyScheduleId: scheduleId.toString(),
                });
                resultReturn.rosterStartAt = startTime;
            }
        }
        // do the same for next event.
        if (nextEvent) {
            logger.debug("Processing next event: " + JSON.stringify(nextEvent), {
                onCallDutyPolicyScheduleId: scheduleId.toString(),
            });
            const userId = nextEvent === null || nextEvent === void 0 ? void 0 : nextEvent.title; // this is user id in string.
            if (userId) {
                logger.debug("Next userId: " + userId, {
                    onCallDutyPolicyScheduleId: scheduleId.toString(),
                });
                resultReturn.nextUserId = new ObjectID(userId);
            }
            // get handOffTime
            const handOffTime = nextEvent === null || nextEvent === void 0 ? void 0 : nextEvent.end; // this is user id in string.
            if (handOffTime) {
                logger.debug("Next handOffTime: " + handOffTime.toISOString(), {
                    onCallDutyPolicyScheduleId: scheduleId.toString(),
                });
                resultReturn.nextHandOffTimeAt = handOffTime;
            }
            // get start time
            const startTime = nextEvent === null || nextEvent === void 0 ? void 0 : nextEvent.start; // this is user id in string.
            if (startTime) {
                logger.debug("Next rosterStartAt: " + startTime.toISOString(), {
                    onCallDutyPolicyScheduleId: scheduleId.toString(),
                });
                resultReturn.nextRosterStartAt = startTime;
            }
        }
        logger.debug("Returning result: " + JSON.stringify(resultReturn), {
            onCallDutyPolicyScheduleId: scheduleId.toString(),
        });
        return resultReturn;
    }
    async getScheduleLayerProps(data) {
        // get schedule layers.
        var _a, _b;
        const scheduleId = data.scheduleId;
        const layers = await OnCallDutyPolicyScheduleLayerService.findBy({
            query: {
                onCallDutyPolicyScheduleId: scheduleId,
            },
            select: {
                order: true,
                name: true,
                description: true,
                startsAt: true,
                restrictionTimes: true,
                rotation: true,
                onCallDutyPolicyScheduleId: true,
                projectId: true,
                handOffTime: true,
            },
            sort: {
                order: SortOrder.Ascending,
            },
            props: {
                isRoot: true,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
        });
        const layerUsers = await OnCallDutyPolicyScheduleLayerUserService.findBy({
            query: {
                onCallDutyPolicyScheduleId: scheduleId,
            },
            select: {
                user: true,
                order: true,
                onCallDutyPolicyScheduleLayerId: true,
            },
            sort: {
                order: SortOrder.Ascending,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
            props: {
                isRoot: true,
            },
        });
        const layerProps = [];
        const scheduleUserIds = [];
        const seenUserIds = new Set();
        for (const layer of layers) {
            const usersForLayer = layerUsers
                .filter((layerUser) => {
                var _a, _b;
                return (((_a = layerUser.onCallDutyPolicyScheduleLayerId) === null || _a === void 0 ? void 0 : _a.toString()) ===
                    ((_b = layer.id) === null || _b === void 0 ? void 0 : _b.toString()));
            })
                .map((layerUser) => {
                return layerUser.user;
            })
                .filter((user) => {
                return Boolean(user);
            });
            for (const user of usersForLayer) {
                const idStr = ((_a = user.id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
                if (idStr && !seenUserIds.has(idStr)) {
                    seenUserIds.add(idStr);
                    scheduleUserIds.push(user.id);
                }
            }
            layerProps.push({
                users: usersForLayer,
                startDateTimeOfLayer: layer.startsAt,
                restrictionTimes: layer.restrictionTimes,
                rotation: layer.rotation,
                handOffTime: layer.handOffTime,
            });
        }
        const projectId = ((_b = layers[0]) === null || _b === void 0 ? void 0 : _b.projectId) || null;
        return { layerProps, projectId, scheduleUserIds };
    }
    async fetchOverridesForSchedule(data) {
        if (data.scheduleUserIds.length === 0) {
            return [];
        }
        /*
         * When a policy context is provided, include overrides scoped to that
         * policy plus global overrides. Without a policy context (e.g. schedule
         * roster refresh, dashboard preview, incoming-call routing), only global
         * overrides apply — a policy-specific override from one policy must not
         * affect schedule resolution for a different policy.
         */
        const overrides = await OnCallDutyPolicyUserOverrideService.findBy({
            query: {
                projectId: data.projectId,
                startsAt: QueryHelper.lessThanEqualTo(data.windowEnd),
                endsAt: QueryHelper.greaterThanEqualTo(data.windowStart),
                onCallDutyPolicyId: data.currentOnCallDutyPolicyId
                    ? QueryHelper.equalToOrNull(data.currentOnCallDutyPolicyId)
                    : QueryHelper.isNull(),
            },
            select: {
                startsAt: true,
                endsAt: true,
                overrideUserId: true,
                routeAlertsToUserId: true,
                onCallDutyPolicyId: true,
            },
            sort: {
                startsAt: SortOrder.Ascending,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
            props: {
                isRoot: true,
            },
        });
        const scheduleUserIdSet = new Set(data.scheduleUserIds.map((id) => {
            return id.toString();
        }));
        return overrides
            .filter((o) => {
            var _a;
            const overrideUserId = ((_a = o.overrideUserId) === null || _a === void 0 ? void 0 : _a.toString()) || "";
            return scheduleUserIdSet.has(overrideUserId);
        })
            .map((o) => {
            var _a, _b, _c;
            return {
                overrideUserId: ((_a = o.overrideUserId) === null || _a === void 0 ? void 0 : _a.toString()) || "",
                routeAlertsToUserId: ((_b = o.routeAlertsToUserId) === null || _b === void 0 ? void 0 : _b.toString()) || "",
                startsAt: o.startsAt,
                endsAt: o.endsAt,
                onCallDutyPolicyId: ((_c = o.onCallDutyPolicyId) === null || _c === void 0 ? void 0 : _c.toString()) || null,
            };
        });
    }
    async getEventByIndexInSchedule(data) {
        var _a;
        logger.debug("getEventByIndexInSchedule called with data: " + JSON.stringify(data), {
            onCallDutyPolicyScheduleId: data.scheduleId.toString(),
        });
        const { layerProps, projectId, scheduleUserIds } = await this.getScheduleLayerProps({
            scheduleId: data.scheduleId,
        });
        logger.debug("Layer properties fetched: " + JSON.stringify(layerProps), {
            onCallDutyPolicyScheduleId: data.scheduleId.toString(),
        });
        if (layerProps.length === 0) {
            logger.debug("No layers found for scheduleId: " + data.scheduleId.toString(), {
                onCallDutyPolicyScheduleId: data.scheduleId.toString(),
            });
            return [];
        }
        const currentStartTime = OneUptimeDate.getCurrentDate();
        logger.debug("Current start time: " + currentStartTime.toISOString(), {
            onCallDutyPolicyScheduleId: data.scheduleId.toString(),
        });
        const currentEndTime = OneUptimeDate.addRemoveYears(currentStartTime, 1);
        logger.debug("Current end time: " + currentEndTime.toISOString(), {
            onCallDutyPolicyScheduleId: data.scheduleId.toString(),
        });
        const numberOfEventsToGet = data.getNumberOfEvents;
        logger.debug("Number of events to get: " + numberOfEventsToGet, {
            onCallDutyPolicyScheduleId: data.scheduleId.toString(),
        });
        let events = this.layerUtil.getMultiLayerEvents({
            layers: layerProps,
            calendarStartDate: currentStartTime,
            calendarEndDate: currentEndTime,
        }, {
            getNumberOfEvents: numberOfEventsToGet,
        });
        if (projectId && events.length > 0) {
            const overrides = await this.fetchOverridesForSchedule({
                projectId,
                scheduleUserIds,
                windowStart: currentStartTime,
                windowEnd: currentEndTime,
                currentOnCallDutyPolicyId: data.onCallDutyPolicyId,
            });
            if (overrides.length > 0) {
                events = UserOverrideUtil.applyOverridesToEvents({
                    events,
                    overrides,
                    currentOnCallDutyPolicyId: (_a = data.onCallDutyPolicyId) === null || _a === void 0 ? void 0 : _a.toString(),
                });
            }
        }
        logger.debug("Events fetched: " + JSON.stringify(events), {
            onCallDutyPolicyScheduleId: data.scheduleId.toString(),
        });
        return events;
    }
    async getCurrentUserIdInSchedule(scheduleId, options) {
        var _a;
        const { layerProps, projectId, scheduleUserIds } = await this.getScheduleLayerProps({
            scheduleId: scheduleId,
        });
        if (layerProps.length === 0) {
            return null;
        }
        const currentStartTime = OneUptimeDate.getCurrentDate();
        const currentEndTime = OneUptimeDate.addRemoveSeconds(currentStartTime, 1);
        let events = this.layerUtil.getMultiLayerEvents({
            layers: layerProps,
            calendarStartDate: currentStartTime,
            calendarEndDate: currentEndTime,
        }, {
            getNumberOfEvents: 1,
        });
        if (projectId && events.length > 0) {
            const overrides = await this.fetchOverridesForSchedule({
                projectId,
                scheduleUserIds,
                windowStart: currentStartTime,
                windowEnd: currentEndTime,
                currentOnCallDutyPolicyId: options === null || options === void 0 ? void 0 : options.onCallDutyPolicyId,
            });
            if (overrides.length > 0) {
                events = UserOverrideUtil.applyOverridesToEvents({
                    events,
                    overrides,
                    currentOnCallDutyPolicyId: (_a = options === null || options === void 0 ? void 0 : options.onCallDutyPolicyId) === null || _a === void 0 ? void 0 : _a.toString(),
                });
            }
        }
        const currentEvent = events[0] || null;
        if (!currentEvent) {
            return null;
        }
        const userId = currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent.title; // this is user id in string.
        if (!userId) {
            return null;
        }
        return new ObjectID(userId);
    }
}
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID, Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "getCurrentUserIdInSchedule", null);
export default new Service();
//# sourceMappingURL=OnCallDutyPolicyScheduleService.js.map