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 ObjectID from "../../Types/ObjectID";
import Version from "../../Types/Version";
import Model, { ProbeConnectionStatus, } from "../../Models/DatabaseModels/Probe";
import ProbeOwnerUserService from "./ProbeOwnerUserService";
import LIMIT_MAX, { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
import ProbeOwnerTeamService from "./ProbeOwnerTeamService";
import TeamMemberService from "./TeamMemberService";
import BadDataException from "../../Types/Exception/BadDataException";
import ProjectService from "./ProjectService";
import OneUptimeDate from "../../Types/Date";
import UserNotificationSettingService from "./UserNotificationSettingService";
import NotificationSettingEventType from "../../Types/NotificationSetting/NotificationSettingEventType";
import logger from "../Utils/Logger";
import EmailTemplateType from "../../Types/Email/EmailTemplateType";
import DatabaseConfig from "../DatabaseConfig";
import URL from "../../Types/API/URL";
import MonitorService from "./MonitorService";
import PushNotificationUtil from "../Utils/PushNotificationUtil";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
import { IsBillingEnabled } from "../EnvironmentConfig";
import GlobalCache from "../Infrastructure/GlobalCache";
import { createWhatsAppMessageFromTemplate } from "../Utils/WhatsAppTemplateUtil";
export class Service extends DatabaseService {
    constructor() {
        super(Model);
    }
    async saveLastAliveInCache(probeId, lastAlive) {
        if (!probeId) {
            throw new BadDataException("probeId is required");
        }
        try {
            await GlobalCache.setString("probe-last-alive", probeId.toString(), OneUptimeDate.toString(lastAlive));
        }
        catch (err) {
            logger.error("Error in saving last alive in cache", {
                projectId: probeId === null || probeId === void 0 ? void 0 : probeId.toString(),
            });
            logger.error(err, { projectId: probeId === null || probeId === void 0 ? void 0 : probeId.toString() });
        }
    }
    async shouldSaveLastAlive(probeId) {
        const now = OneUptimeDate.getCurrentDate();
        try {
            // before we hit the database, we need to check if the lastAlive was updated in Global Cache.
            const previousLastAliveCheck = await GlobalCache.getString("probe-last-alive", probeId.toString());
            if (!previousLastAliveCheck) {
                await this.saveLastAliveInCache(probeId, now);
                return true;
            }
            const previousLastAliveCheckDate = OneUptimeDate.fromString(previousLastAliveCheck);
            // if this date is within 30 seconds of current date, then we will not update the last alive.
            if (previousLastAliveCheckDate) {
                const diff = OneUptimeDate.getDifferenceInSeconds(now, previousLastAliveCheckDate);
                if (diff < 30) {
                    return false;
                }
            }
            await this.saveLastAliveInCache(probeId, now);
        }
        catch (err) {
            // failed to hit the cache, so we will hit the database
            logger.error("Error in getting last alive from cache", {
                probeId: probeId === null || probeId === void 0 ? void 0 : probeId.toString(),
            });
            logger.error(err, { probeId: probeId === null || probeId === void 0 ? void 0 : probeId.toString() });
        }
        return true;
    }
    async updateLastAlive(probeId) {
        if (!probeId) {
            throw new BadDataException("probeId is required");
        }
        const shouldSaveLastAlive = await this.shouldSaveLastAlive(probeId);
        if (!shouldSaveLastAlive) {
            return;
        }
        const now = OneUptimeDate.getCurrentDate();
        await this.updateOneById({
            id: probeId,
            data: {
                lastAlive: now,
            },
            props: {
                isRoot: true,
            },
        });
    }
    async onBeforeCreate(createBy) {
        if (!createBy.data.key) {
            createBy.data.key = ObjectID.generate().toString();
        }
        if (!createBy.data.probeVersion) {
            createBy.data.probeVersion = new Version("1.0.0");
        }
        return { createBy: createBy, carryForward: [] };
    }
    async getOwners(probeId) {
        if (!probeId) {
            throw new BadDataException("probeId is required");
        }
        const ownerUsers = await ProbeOwnerUserService.findBy({
            query: {
                probeId: probeId,
            },
            select: {
                _id: true,
                user: {
                    _id: true,
                    email: true,
                    name: true,
                    timezone: true,
                },
            },
            props: {
                isRoot: true,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
        });
        const ownerTeams = await ProbeOwnerTeamService.findBy({
            query: {
                probeId: probeId,
            },
            select: {
                _id: true,
                teamId: true,
            },
            skip: 0,
            limit: LIMIT_PER_PROJECT,
            props: {
                isRoot: true,
            },
        });
        const users = ownerUsers.map((ownerUser) => {
            return ownerUser.user;
        }) || [];
        if (ownerTeams.length > 0) {
            const teamIds = ownerTeams.map((ownerTeam) => {
                return ownerTeam.teamId;
            }) || [];
            const teamUsers = await TeamMemberService.getUsersInTeams(teamIds);
            for (const teamUser of teamUsers) {
                //check if the user is already added.
                const isUserAlreadyAdded = users.find((user) => {
                    return user.id.toString() === teamUser.id.toString();
                });
                if (!isUserAlreadyAdded) {
                    users.push(teamUser);
                }
            }
        }
        return users;
    }
    async onBeforeUpdate(updateBy) {
        const carryForward = {
            probesToNotifyOwners: [],
        };
        if (updateBy.data.connectionStatus && updateBy.query._id) {
            const probes = await this.findBy({
                query: updateBy.query,
                props: {
                    isRoot: true,
                },
                select: {
                    _id: true,
                    connectionStatus: true,
                },
                skip: 0,
                limit: LIMIT_MAX,
            });
            const probesToNotifyOwners = probes.filter((probe) => {
                return (probe.connectionStatus &&
                    probe.connectionStatus !== updateBy.data.connectionStatus);
            });
            carryForward.probesToNotifyOwners = probesToNotifyOwners;
        }
        return { updateBy: updateBy, carryForward };
    }
    async onUpdateSuccess(onUpdate, _updatedItemIds) {
        if (onUpdate.carryForward &&
            onUpdate.carryForward.probesToNotifyOwners.length > 0) {
            for (const probe of onUpdate.carryForward.probesToNotifyOwners) {
                await MonitorService.refreshProbeStatus(probe.id);
                await this.notifyOwnersOnStatusChange({
                    probeId: probe.id,
                });
            }
        }
        return Promise.resolve(onUpdate);
    }
    async notifyOwnersOnStatusChange(data) {
        var _a, _b, _c, _d;
        const probe = await this.findOneById({
            id: data.probeId,
            props: {
                isRoot: true,
            },
            select: {
                _id: true,
                lastAlive: true,
                connectionStatus: true,
                isGlobalProbe: true,
                name: true,
                description: true,
                projectId: true,
                project: {
                    name: true,
                },
            },
        });
        if (!probe || !probe.id) {
            throw new BadDataException("Probe not found");
        }
        if (!probe.projectId) {
            return; // might be global probe. Do not notify.
        }
        if (probe.isGlobalProbe && IsBillingEnabled) {
            return; // do not notify for global probes.
        }
        // notify the probe owner
        let owners = await this.getOwners(probe.id);
        let doesResourceHasOwners = true;
        if (owners.length === 0) {
            doesResourceHasOwners = false;
            // find project owners.
            owners = await ProjectService.getOwners(probe.projectId);
        }
        if (owners.length === 0) {
            return; // no owners to notify.
        }
        const connectionStatus = probe.connectionStatus === ProbeConnectionStatus.Connected
            ? "Connected"
            : "Disconnected";
        for (const user of owners) {
            try {
                const vars = {
                    title: `${probe.name} is ${connectionStatus}`,
                    probeName: probe.name || "Probe",
                    probeDescription: probe.description || "No description provided",
                    projectName: ((_a = probe.project) === null || _a === void 0 ? void 0 : _a.name) || "Project",
                    probeStatus: connectionStatus || "Unknown",
                    lastAlive: OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
                        date: probe.lastAlive || OneUptimeDate.getCurrentDate(),
                        timezones: user.timezone ? [user.timezone] : [],
                    }),
                    viewProbesLink: (await this.getLinkInDashboard(probe.projectId, probe.id)).toString(),
                };
                if (doesResourceHasOwners === true) {
                    vars["isOwner"] = "true";
                }
                const emailMessage = {
                    templateType: EmailTemplateType.ProbeConnectionStatusChange,
                    vars: vars,
                    subject: `[Probe ${connectionStatus}] ${probe.name}`,
                };
                const sms = {
                    message: `This is a message from OneUptime. Probe ${probe.name} is ${connectionStatus}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard.`,
                };
                const callMessage = {
                    data: [
                        {
                            sayMessage: `This is a message from OneUptime. Probe ${probe.name} is ${connectionStatus}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard. Good bye.`,
                        },
                    ],
                };
                const pushMessageParams = {
                    probeName: probe.name,
                    projectName: ((_b = probe.project) === null || _b === void 0 ? void 0 : _b.name) || "Project",
                    connectionStatus: connectionStatus,
                };
                if (vars["viewProbesLink"]) {
                    pushMessageParams.clickAction = vars["viewProbesLink"];
                }
                const pushMessage = PushNotificationUtil.createProbeStatusChangedNotification(pushMessageParams);
                const eventType = NotificationSettingEventType.SEND_PROBE_STATUS_CHANGED_OWNER_NOTIFICATION;
                const whatsAppMessage = createWhatsAppMessageFromTemplate({
                    eventType,
                    templateVariables: {
                        probe_name: probe.name,
                        probe_status: connectionStatus,
                        probe_link: vars["viewProbesLink"] || "",
                    },
                });
                await UserNotificationSettingService.sendUserNotification({
                    userId: user.id,
                    projectId: probe.projectId,
                    emailEnvelope: emailMessage,
                    smsMessage: sms,
                    callRequestMessage: callMessage,
                    pushNotificationMessage: pushMessage,
                    whatsAppMessage,
                    eventType,
                });
            }
            catch (e) {
                logger.error("Error in sending incident created resource notification", { projectId: (_c = probe.projectId) === null || _c === void 0 ? void 0 : _c.toString() });
                logger.error(e, {
                    projectId: (_d = probe.projectId) === null || _d === void 0 ? void 0 : _d.toString(),
                });
            }
        }
    }
    async getLinkInDashboard(projectId, probeId) {
        const dashboardUrl = await DatabaseConfig.getDashboardUrl();
        return URL.fromString(dashboardUrl.toString()).addRoute(`/${projectId.toString()}/settings/probes/${probeId.toString()}`);
    }
}
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "onBeforeCreate", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID]),
    __metadata("design:returntype", Promise)
], Service.prototype, "getOwners", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "onBeforeUpdate", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Array]),
    __metadata("design:returntype", Promise)
], Service.prototype, "onUpdateSuccess", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "notifyOwnersOnStatusChange", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID,
        ObjectID]),
    __metadata("design:returntype", Promise)
], Service.prototype, "getLinkInDashboard", null);
export default new Service();
//# sourceMappingURL=ProbeService.js.map