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, { AIAgentConnectionStatus, } from "../../Models/DatabaseModels/AIAgent";
import AIAgentOwnerUserService from "./AIAgentOwnerUserService";
import LIMIT_MAX, { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
import AIAgentOwnerTeamService from "./AIAgentOwnerTeamService";
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 PushNotificationUtil from "../Utils/PushNotificationUtil";
import { createWhatsAppMessageFromTemplate } from "../Utils/WhatsAppTemplateUtil";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
import { IsBillingEnabled } from "../EnvironmentConfig";
import GlobalCache from "../Infrastructure/GlobalCache";
import QueryHelper from "../Types/Database/QueryHelper";
export class Service extends DatabaseService {
    constructor() {
        super(Model);
    }
    async saveLastAliveInCache(aiAgentId, lastAlive) {
        if (!aiAgentId) {
            throw new BadDataException("aiAgentId is required");
        }
        try {
            await GlobalCache.setString("ai-agent-last-alive", aiAgentId.toString(), OneUptimeDate.toString(lastAlive));
        }
        catch (err) {
            logger.error("Error in saving last alive in cache", {
                aiAgentId: aiAgentId === null || aiAgentId === void 0 ? void 0 : aiAgentId.toString(),
            });
            logger.error(err, { aiAgentId: aiAgentId === null || aiAgentId === void 0 ? void 0 : aiAgentId.toString() });
        }
    }
    async shouldSaveLastAlive(aiAgentId) {
        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("ai-agent-last-alive", aiAgentId.toString());
            if (!previousLastAliveCheck) {
                await this.saveLastAliveInCache(aiAgentId, 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(aiAgentId, now);
        }
        catch (err) {
            // failed to hit the cache, so we will hit the database
            logger.error("Error in getting last alive from cache", {
                aiAgentId: aiAgentId === null || aiAgentId === void 0 ? void 0 : aiAgentId.toString(),
            });
            logger.error(err, { aiAgentId: aiAgentId === null || aiAgentId === void 0 ? void 0 : aiAgentId.toString() });
        }
        return true;
    }
    async updateLastAlive(aiAgentId) {
        if (!aiAgentId) {
            throw new BadDataException("aiAgentId is required");
        }
        const shouldSaveLastAlive = await this.shouldSaveLastAlive(aiAgentId);
        if (!shouldSaveLastAlive) {
            return;
        }
        const now = OneUptimeDate.getCurrentDate();
        await this.updateOneById({
            id: aiAgentId,
            data: {
                lastAlive: now,
            },
            props: {
                isRoot: true,
            },
        });
    }
    async onBeforeCreate(createBy) {
        if (!createBy.data.key) {
            createBy.data.key = ObjectID.generate().toString();
        }
        if (!createBy.data.aiAgentVersion) {
            createBy.data.aiAgentVersion = new Version("1.0.0");
        }
        // When creating a new AI Agent, set it as default by default
        if (createBy.data.isDefault === undefined) {
            createBy.data.isDefault = true;
        }
        // If this agent is being set as default, unset other defaults in the same project
        if (createBy.data.isDefault && createBy.data.projectId) {
            await this.updateBy({
                query: {
                    projectId: createBy.data.projectId,
                    isDefault: true,
                },
                data: {
                    isDefault: false,
                },
                props: {
                    isRoot: true,
                },
                limit: LIMIT_MAX,
                skip: 0,
            });
        }
        return { createBy: createBy, carryForward: [] };
    }
    async getOwners(aiAgentId) {
        if (!aiAgentId) {
            throw new BadDataException("aiAgentId is required");
        }
        const ownerUsers = await AIAgentOwnerUserService.findBy({
            query: {
                aiAgentId: aiAgentId,
            },
            select: {
                _id: true,
                user: {
                    _id: true,
                    email: true,
                    name: true,
                    timezone: true,
                },
            },
            props: {
                isRoot: true,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
        });
        const ownerTeams = await AIAgentOwnerTeamService.findBy({
            query: {
                aiAgentId: aiAgentId,
            },
            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 = {
            aiAgentsToNotifyOwners: [],
        };
        if (updateBy.data.connectionStatus && updateBy.query._id) {
            const aiAgents = await this.findBy({
                query: updateBy.query,
                props: {
                    isRoot: true,
                },
                select: {
                    _id: true,
                    connectionStatus: true,
                },
                skip: 0,
                limit: LIMIT_MAX,
            });
            const aiAgentsToNotifyOwners = aiAgents.filter((aiAgent) => {
                return (aiAgent.connectionStatus &&
                    aiAgent.connectionStatus !== updateBy.data.connectionStatus);
            });
            carryForward.aiAgentsToNotifyOwners = aiAgentsToNotifyOwners;
        }
        // If setting isDefault to true, we need to unset other defaults in the same project
        if (updateBy.data.isDefault === true) {
            // Get the items being updated to find their project IDs
            const itemsToUpdate = await this.findBy({
                query: updateBy.query,
                select: {
                    _id: true,
                    projectId: true,
                },
                props: {
                    isRoot: true,
                },
                limit: LIMIT_MAX,
                skip: 0,
            });
            // Collect unique project IDs
            const projectIds = new Set();
            const itemIds = new Set();
            for (const item of itemsToUpdate) {
                if (item.projectId) {
                    projectIds.add(item.projectId.toString());
                }
                if (item._id) {
                    itemIds.add(item._id);
                }
            }
            // For each project, unset the default on other agents
            for (const projectIdStr of projectIds) {
                const projectId = new ObjectID(projectIdStr);
                await this.updateBy({
                    query: {
                        projectId: projectId,
                        isDefault: true,
                        _id: QueryHelper.notInOrNull(Array.from(itemIds)),
                    },
                    data: {
                        isDefault: false,
                    },
                    props: {
                        isRoot: true,
                    },
                    limit: LIMIT_MAX,
                    skip: 0,
                });
            }
        }
        return { updateBy: updateBy, carryForward };
    }
    async onUpdateSuccess(onUpdate, _updatedItemIds) {
        if (onUpdate.carryForward &&
            onUpdate.carryForward.aiAgentsToNotifyOwners.length > 0) {
            for (const aiAgent of onUpdate.carryForward.aiAgentsToNotifyOwners) {
                await this.notifyOwnersOnStatusChange({
                    aiAgentId: aiAgent.id,
                });
            }
        }
        return Promise.resolve(onUpdate);
    }
    async notifyOwnersOnStatusChange(data) {
        var _a, _b, _c, _d;
        const aiAgent = await this.findOneById({
            id: data.aiAgentId,
            props: {
                isRoot: true,
            },
            select: {
                _id: true,
                lastAlive: true,
                connectionStatus: true,
                isGlobalAIAgent: true,
                name: true,
                description: true,
                projectId: true,
                project: {
                    name: true,
                },
            },
        });
        if (!aiAgent || !aiAgent.id) {
            throw new BadDataException("AI Agent not found");
        }
        if (!aiAgent.projectId) {
            return; // might be global AI agent. Do not notify.
        }
        if (aiAgent.isGlobalAIAgent && IsBillingEnabled) {
            return; // do not notify for global AI agents.
        }
        // notify the AI agent owner
        let owners = await this.getOwners(aiAgent.id);
        let doesResourceHasOwners = true;
        if (owners.length === 0) {
            doesResourceHasOwners = false;
            // find project owners.
            owners = await ProjectService.getOwners(aiAgent.projectId);
        }
        if (owners.length === 0) {
            return; // no owners to notify.
        }
        const connectionStatus = aiAgent.connectionStatus === AIAgentConnectionStatus.Connected
            ? "Connected"
            : "Disconnected";
        for (const user of owners) {
            try {
                const vars = {
                    title: `${aiAgent.name} is ${connectionStatus}`,
                    aiAgentName: aiAgent.name || "AI Agent",
                    aiAgentDescription: aiAgent.description || "No description provided",
                    projectName: ((_a = aiAgent.project) === null || _a === void 0 ? void 0 : _a.name) || "Project",
                    aiAgentStatus: connectionStatus || "Unknown",
                    lastAlive: OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
                        date: aiAgent.lastAlive || OneUptimeDate.getCurrentDate(),
                        timezones: user.timezone ? [user.timezone] : [],
                    }),
                    viewAIAgentsLink: (await this.getLinkInDashboard(aiAgent.projectId, aiAgent.id)).toString(),
                };
                if (doesResourceHasOwners === true) {
                    vars["isOwner"] = "true";
                }
                const emailMessage = {
                    templateType: EmailTemplateType.AIAgentConnectionStatusChange,
                    vars: vars,
                    subject: `[AI Agent ${connectionStatus}] ${aiAgent.name}`,
                };
                const sms = {
                    message: `This is a message from OneUptime. AI Agent ${aiAgent.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. AI Agent ${aiAgent.name} is ${connectionStatus}. To unsubscribe from this notification go to User Settings in OneUptime Dashboard. Good bye.`,
                        },
                    ],
                };
                const pushMessageParams = {
                    aiAgentName: aiAgent.name,
                    projectName: ((_b = aiAgent.project) === null || _b === void 0 ? void 0 : _b.name) || "Project",
                    connectionStatus: connectionStatus,
                };
                if (vars["viewAIAgentsLink"]) {
                    pushMessageParams.clickAction = vars["viewAIAgentsLink"];
                }
                const pushMessage = PushNotificationUtil.createAIAgentStatusChangedNotification(pushMessageParams);
                const eventType = NotificationSettingEventType.SEND_AI_AGENT_STATUS_CHANGED_OWNER_NOTIFICATION;
                const whatsAppMessage = createWhatsAppMessageFromTemplate({
                    eventType,
                    templateVariables: {
                        ai_agent_name: aiAgent.name,
                        ai_agent_status: connectionStatus,
                        ai_agent_link: vars["viewAIAgentsLink"] || "",
                    },
                });
                await UserNotificationSettingService.sendUserNotification({
                    userId: user.id,
                    projectId: aiAgent.projectId,
                    emailEnvelope: emailMessage,
                    smsMessage: sms,
                    callRequestMessage: callMessage,
                    pushNotificationMessage: pushMessage,
                    whatsAppMessage,
                    eventType,
                });
            }
            catch (e) {
                logger.error("Error in sending AI agent status changed resource notification", { projectId: (_c = aiAgent.projectId) === null || _c === void 0 ? void 0 : _c.toString() });
                logger.error(e, {
                    projectId: (_d = aiAgent.projectId) === null || _d === void 0 ? void 0 : _d.toString(),
                });
            }
        }
    }
    async getLinkInDashboard(projectId, aiAgentId) {
        const dashboardUrl = await DatabaseConfig.getDashboardUrl();
        return URL.fromString(dashboardUrl.toString()).addRoute(`/${projectId.toString()}/settings/ai-agents/${aiAgentId.toString()}`);
    }
    async getAIAgentForProject(projectId) {
        // First try to get the default AI agent for the project
        let agent = await this.findOneBy({
            query: {
                projectId: projectId,
                isDefault: true,
            },
            select: {
                _id: true,
                name: true,
                description: true,
                key: true,
                aiAgentVersion: true,
                connectionStatus: true,
                isGlobalAIAgent: true,
                lastAlive: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (agent) {
            return agent;
        }
        // If no default agent, get any global AI agent
        agent = await this.findOneBy({
            query: {
                projectId: QueryHelper.isNull(),
                isGlobalAIAgent: true,
            },
            select: {
                _id: true,
                name: true,
                description: true,
                key: true,
                aiAgentVersion: true,
                connectionStatus: true,
                isGlobalAIAgent: true,
                lastAlive: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (agent) {
            return agent;
        }
        return null;
    }
}
__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);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID]),
    __metadata("design:returntype", Promise)
], Service.prototype, "getAIAgentForProject", null);
export default new Service();
//# sourceMappingURL=AIAgentService.js.map