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 { applyIncidentEpisodeSelfPrivacyFilter } from "../Utils/IncidentEpisode/IncidentEpisodePrivacyFilter";
import DatabaseService from "./DatabaseService";
import IncidentStateService from "./IncidentStateService";
import BadDataException from "../../Types/Exception/BadDataException";
import ObjectID from "../../Types/ObjectID";
import Model from "../../Models/DatabaseModels/IncidentEpisode";
import SortOrder from "../../Types/BaseDatabase/SortOrder";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
import logger from "../Utils/Logger";
import IncidentEpisodeStateTimeline from "../../Models/DatabaseModels/IncidentEpisodeStateTimeline";
import IncidentEpisodeStateTimelineService from "./IncidentEpisodeStateTimelineService";
import { IsBillingEnabled } from "../EnvironmentConfig";
import OneUptimeDate from "../../Types/Date";
import IncidentEpisodeFeedService from "./IncidentEpisodeFeedService";
import { IncidentEpisodeFeedEventType } from "../../Models/DatabaseModels/IncidentEpisodeFeed";
import { Red500, Yellow500, Purple500 } from "../../Types/BrandColors";
import URL from "../../Types/API/URL";
import DatabaseConfig from "../DatabaseConfig";
import IncidentSeverityService from "./IncidentSeverityService";
import IncidentEpisodeMemberService from "./IncidentEpisodeMemberService";
import IncidentEpisodeOwnerUserService from "./IncidentEpisodeOwnerUserService";
import IncidentEpisodeOwnerTeamService from "./IncidentEpisodeOwnerTeamService";
import TeamMemberService from "./TeamMemberService";
import IncidentEpisodeOwnerUser from "../../Models/DatabaseModels/IncidentEpisodeOwnerUser";
import IncidentEpisodeOwnerTeam from "../../Models/DatabaseModels/IncidentEpisodeOwnerTeam";
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
import IncidentEpisodeWorkspaceMessages from "../Utils/Workspace/WorkspaceMessages/IncidentEpisode";
import IncidentService from "./IncidentService";
import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
import UserNotificationEventType from "../../Types/UserNotification/UserNotificationEventType";
import IncidentGroupingRuleService from "./IncidentGroupingRuleService";
import ProjectService from "./ProjectService";
import IncidentEpisodeLabelRuleEngineService from "./IncidentEpisodeLabelRuleEngineService";
import IncidentEpisodeOnCallRuleEngineService from "./IncidentEpisodeOnCallRuleEngineService";
import IncidentEpisodeOwnerRuleEngineService from "./IncidentEpisodeOwnerRuleEngineService";
import IncidentEpisodePrivacyRuleEngineService from "./IncidentEpisodePrivacyRuleEngineService";
export class Service extends DatabaseService {
    constructor() {
        super(Model);
        if (IsBillingEnabled) {
            this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
        }
    }
    async onBeforeFind(findBy) {
        findBy.query = applyIncidentEpisodeSelfPrivacyFilter(findBy.query, findBy.props);
        return { findBy, carryForward: null };
    }
    async countBy(countBy) {
        countBy.query = applyIncidentEpisodeSelfPrivacyFilter(countBy.query, countBy.props);
        return super.countBy(countBy);
    }
    async onBeforeUpdate(updateBy) {
        updateBy.query = applyIncidentEpisodeSelfPrivacyFilter(updateBy.query, updateBy.props);
        return { updateBy, carryForward: null };
    }
    async onBeforeDelete(deleteBy) {
        deleteBy.query = applyIncidentEpisodeSelfPrivacyFilter(deleteBy.query, deleteBy.props);
        return { deleteBy, carryForward: null };
    }
    async onBeforeCreate(createBy) {
        var _a;
        if (!createBy.props.tenantId && !createBy.props.isRoot) {
            throw new BadDataException("ProjectId required to create incident episode.");
        }
        const projectId = createBy.props.tenantId || createBy.data.projectId;
        // Get the created state for episodes
        const incidentState = await IncidentStateService.findOneBy({
            query: {
                projectId: projectId,
                isCreatedState: true,
            },
            select: {
                _id: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!incidentState || !incidentState.id) {
            throw new BadDataException("Created incident state not found for this project. Please add created incident state from settings.");
        }
        createBy.data.currentIncidentStateId = incidentState.id;
        // Auto-generate episode number
        const episodeCounterResult = await ProjectService.incrementAndGetIncidentEpisodeCounter(projectId);
        createBy.data.episodeNumber = episodeCounterResult.counter;
        createBy.data.episodeNumberWithPrefix = episodeCounterResult.prefix
            ? `${episodeCounterResult.prefix}${episodeCounterResult.counter}`
            : `#${episodeCounterResult.counter}`;
        // Set initial lastIncidentAddedAt
        if (!createBy.data.lastIncidentAddedAt) {
            createBy.data.lastIncidentAddedAt = OneUptimeDate.getCurrentDate();
        }
        // Set declaredAt if not provided
        if (!createBy.data.declaredAt) {
            createBy.data.declaredAt = OneUptimeDate.getCurrentDate();
        }
        // Copy showEpisodeOnStatusPage from grouping rule if available
        if (createBy.data.incidentGroupingRuleId) {
            const groupingRule = await IncidentGroupingRuleService.findOneById({
                id: createBy.data.incidentGroupingRuleId,
                select: {
                    showEpisodeOnStatusPage: true,
                },
                props: {
                    isRoot: true,
                },
            });
            if (groupingRule) {
                createBy.data.isVisibleOnStatusPage =
                    (_a = groupingRule.showEpisodeOnStatusPage) !== null && _a !== void 0 ? _a : true;
            }
        }
        return { createBy, carryForward: null };
    }
    async onCreateSuccess(_onCreate, createdItem) {
        if (!createdItem.projectId) {
            throw new BadDataException("projectId is required");
        }
        if (!createdItem.id) {
            throw new BadDataException("id is required");
        }
        if (!createdItem.currentIncidentStateId) {
            throw new BadDataException("currentIncidentStateId is required");
        }
        // Create initial state timeline entry
        Promise.resolve()
            .then(async () => {
            var _a, _b;
            /*
             * Apply privacy rules BEFORE workspace operations so the workspace
             * channel is created with the correct privacy setting. This may set
             * createdItem.isPrivate=true in memory.
             */
            try {
                await IncidentEpisodePrivacyRuleEngineService.applyRulesToEpisode(createdItem);
            }
            catch (error) {
                logger.error(`Apply incident episode privacy rules failed in IncidentEpisodeService.onCreateSuccess: ${error}`, {
                    projectId: (_a = createdItem.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    incidentEpisodeId: (_b = createdItem.id) === null || _b === void 0 ? void 0 : _b.toString(),
                });
            }
        })
            .then(async () => {
            var _a, _b;
            try {
                if (createdItem.projectId && createdItem.id) {
                    await this.handleEpisodeWorkspaceOperationsAsync(createdItem);
                }
            }
            catch (error) {
                logger.error(`Workspace operations failed in IncidentEpisodeService.onCreateSuccess: ${error}`, {
                    projectId: (_a = createdItem.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    incidentEpisodeId: (_b = createdItem.id) === null || _b === void 0 ? void 0 : _b.toString(),
                });
            }
        })
            .then(async () => {
            var _a, _b;
            try {
                await this.changeEpisodeState({
                    projectId: createdItem.projectId,
                    episodeId: createdItem.id,
                    incidentStateId: createdItem.currentIncidentStateId,
                    notifyOwners: false,
                    rootCause: undefined,
                    props: {
                        isRoot: true,
                    },
                });
            }
            catch (error) {
                logger.error(`Handle episode state change failed in IncidentEpisodeService.onCreateSuccess: ${error}`, {
                    projectId: (_a = createdItem.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    incidentEpisodeId: (_b = createdItem.id) === null || _b === void 0 ? void 0 : _b.toString(),
                });
            }
        })
            .then(async () => {
            var _a, _b;
            try {
                await this.createEpisodeCreatedFeed(createdItem);
            }
            catch (error) {
                logger.error(`Create episode feed failed in IncidentEpisodeService.onCreateSuccess: ${error}`, {
                    projectId: (_a = createdItem.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    incidentEpisodeId: (_b = createdItem.id) === null || _b === void 0 ? void 0 : _b.toString(),
                });
            }
        })
            .then(async () => {
            var _a, _b;
            // Apply owner rules: add matched owner users/teams to the episode.
            try {
                await IncidentEpisodeOwnerRuleEngineService.applyRulesToEpisode(createdItem);
            }
            catch (error) {
                logger.error(`Apply incident episode owner rules failed in IncidentEpisodeService.onCreateSuccess: ${error}`, {
                    projectId: (_a = createdItem.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    incidentEpisodeId: (_b = createdItem.id) === null || _b === void 0 ? void 0 : _b.toString(),
                });
            }
        })
            .then(async () => {
            var _a, _b;
            // Apply label rules: attach matched labels to the episode.
            try {
                await IncidentEpisodeLabelRuleEngineService.applyRulesToEpisode(createdItem);
            }
            catch (error) {
                logger.error(`Apply incident episode label rules failed in IncidentEpisodeService.onCreateSuccess: ${error}`, {
                    projectId: (_a = createdItem.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    incidentEpisodeId: (_b = createdItem.id) === null || _b === void 0 ? void 0 : _b.toString(),
                });
            }
        })
            .then(async () => {
            var _a, _b;
            /*
             * Apply on-call rules: merge matched policies into the episode's
             * onCallDutyPolicies before the fan-out below picks them up.
             */
            try {
                await IncidentEpisodeOnCallRuleEngineService.applyRulesToEpisode(createdItem);
            }
            catch (error) {
                logger.error(`Apply incident episode on-call rules failed in IncidentEpisodeService.onCreateSuccess: ${error}`, {
                    projectId: (_a = createdItem.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    incidentEpisodeId: (_b = createdItem.id) === null || _b === void 0 ? void 0 : _b.toString(),
                });
            }
        })
            .then(async () => {
            var _a, _b;
            // Execute on-call duty policies
            try {
                await this.executeEpisodeOnCallDutyPoliciesAsync(createdItem);
            }
            catch (error) {
                logger.error(`On-call duty policy execution failed in IncidentEpisodeService.onCreateSuccess: ${error}`, {
                    projectId: (_a = createdItem.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                    incidentEpisodeId: (_b = createdItem.id) === null || _b === void 0 ? void 0 : _b.toString(),
                });
            }
        })
            .catch((error) => {
            var _a, _b;
            logger.error(`Critical error in IncidentEpisodeService.onCreateSuccess: ${error}`, {
                projectId: (_a = createdItem.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
                incidentEpisodeId: (_b = createdItem.id) === null || _b === void 0 ? void 0 : _b.toString(),
            });
        });
        return createdItem;
    }
    async handleEpisodeWorkspaceOperationsAsync(createdItem) {
        var _a, _b, _c;
        try {
            if (!createdItem.projectId || !createdItem.id) {
                throw new BadDataException("projectId and id are required for workspace operations");
            }
            const workspaceResult = await IncidentEpisodeWorkspaceMessages.createChannelsAndInviteUsersToChannels(Object.assign(Object.assign({ projectId: createdItem.projectId, incidentEpisodeId: createdItem.id, episodeNumber: createdItem.episodeNumber || 0 }, (createdItem.episodeNumberWithPrefix
                ? {
                    episodeNumberWithPrefix: createdItem.episodeNumberWithPrefix,
                }
                : {})), { isPrivate: createdItem.isPrivate === true }));
            if (workspaceResult && ((_a = workspaceResult.channelsCreated) === null || _a === void 0 ? void 0 : _a.length) > 0) {
                await this.updateOneById({
                    id: createdItem.id,
                    data: {
                        postUpdatesToWorkspaceChannels: workspaceResult.channelsCreated || [],
                    },
                    props: {
                        isRoot: true,
                    },
                });
            }
        }
        catch (error) {
            logger.error(`Error in handleEpisodeWorkspaceOperationsAsync: ${error}`, {
                projectId: (_b = createdItem.projectId) === null || _b === void 0 ? void 0 : _b.toString(),
                incidentEpisodeId: (_c = createdItem.id) === null || _c === void 0 ? void 0 : _c.toString(),
            });
            throw error;
        }
    }
    async createEpisodeCreatedFeed(episode) {
        var _a;
        if (!episode.id || !episode.projectId) {
            return;
        }
        let feedInfoInMarkdown = `#### Episode ${episode.episodeNumberWithPrefix || "#" + ((_a = episode.episodeNumber) === null || _a === void 0 ? void 0 : _a.toString())} Created

**${episode.title || "No title provided."}**

`;
        if (episode.description) {
            feedInfoInMarkdown += `${episode.description}\n\n`;
        }
        if (episode.isManuallyCreated) {
            feedInfoInMarkdown += `This episode was manually created.\n\n`;
        }
        const episodeCreateMessageBlocks = await IncidentEpisodeWorkspaceMessages.getIncidentEpisodeCreateMessageBlocks({
            incidentEpisodeId: episode.id,
            projectId: episode.projectId,
        });
        await IncidentEpisodeFeedService.createIncidentEpisodeFeedItem({
            incidentEpisodeId: episode.id,
            projectId: episode.projectId,
            incidentEpisodeFeedEventType: IncidentEpisodeFeedEventType.EpisodeCreated,
            displayColor: Red500,
            feedInfoInMarkdown: feedInfoInMarkdown,
            userId: episode.createdByUserId || undefined,
            workspaceNotification: {
                appendMessageBlocks: episodeCreateMessageBlocks,
                sendWorkspaceNotification: true,
            },
        });
    }
    async executeEpisodeOnCallDutyPoliciesAsync(createdItem) {
        var _a, _b, _c;
        if (!createdItem.id || !createdItem.projectId) {
            return;
        }
        try {
            // Fetch the episode with on-call duty policies since they may not be loaded
            const episodeWithPolicies = await this.findOneById({
                id: createdItem.id,
                select: {
                    onCallDutyPolicies: {
                        _id: true,
                        name: true,
                    },
                },
                props: {
                    isRoot: true,
                },
            });
            if (!((_a = episodeWithPolicies === null || episodeWithPolicies === void 0 ? void 0 : episodeWithPolicies.onCallDutyPolicies) === null || _a === void 0 ? void 0 : _a.length) ||
                episodeWithPolicies.onCallDutyPolicies.length === 0) {
                return;
            }
            // Execute all on-call policies in parallel
            const policyPromises = episodeWithPolicies.onCallDutyPolicies.map((policy) => {
                return OnCallDutyPolicyService.executePolicy(new ObjectID(policy._id), {
                    triggeredByIncidentEpisodeId: createdItem.id,
                    userNotificationEventType: UserNotificationEventType.IncidentEpisodeCreated,
                });
            });
            await Promise.allSettled(policyPromises);
            // Update the flag to indicate on-call policy has been executed
            await this.updateOneById({
                id: createdItem.id,
                data: {
                    isOnCallPolicyExecuted: true,
                },
                props: {
                    isRoot: true,
                },
            });
            // Create feed entry for on-call policy execution
            const policyNames = episodeWithPolicies.onCallDutyPolicies
                .map((policy) => {
                return policy.name || "Unnamed Policy";
            })
                .filter((name) => {
                return Boolean(name);
            });
            let feedInfoInMarkdown = `#### On-Call Policy Executed\n\n`;
            feedInfoInMarkdown += `The following on-call ${policyNames.length === 1 ? "policy has" : "policies have"} been executed for this episode:\n\n`;
            for (const policyName of policyNames) {
                feedInfoInMarkdown += `- ${policyName}\n`;
            }
            await IncidentEpisodeFeedService.createIncidentEpisodeFeedItem({
                incidentEpisodeId: createdItem.id,
                projectId: createdItem.projectId,
                incidentEpisodeFeedEventType: IncidentEpisodeFeedEventType.OnCallPolicy,
                displayColor: Purple500,
                feedInfoInMarkdown: feedInfoInMarkdown,
            });
        }
        catch (error) {
            logger.error(`Error in executeEpisodeOnCallDutyPoliciesAsync: ${error}`, {
                projectId: (_b = createdItem.projectId) === null || _b === void 0 ? void 0 : _b.toString(),
                incidentEpisodeId: (_c = createdItem.id) === null || _c === void 0 ? void 0 : _c.toString(),
            });
            throw error;
        }
    }
    async changeEpisodeState(data) {
        const { projectId, episodeId, incidentStateId, notifyOwners, rootCause, props, cascadeToIncidents, } = data;
        // Get last episode state timeline
        const lastEpisodeStateTimeline = await IncidentEpisodeStateTimelineService.findOneBy({
            query: {
                incidentEpisodeId: episodeId,
                projectId: projectId,
            },
            select: {
                _id: true,
                incidentStateId: true,
            },
            sort: {
                createdAt: SortOrder.Descending,
            },
            props: {
                isRoot: true,
            },
        });
        if (lastEpisodeStateTimeline &&
            lastEpisodeStateTimeline.incidentStateId &&
            lastEpisodeStateTimeline.incidentStateId.toString() ===
                incidentStateId.toString()) {
            return;
        }
        const stateTimeline = new IncidentEpisodeStateTimeline();
        stateTimeline.incidentEpisodeId = episodeId;
        stateTimeline.incidentStateId = incidentStateId;
        stateTimeline.projectId = projectId;
        stateTimeline.isOwnerNotified = !notifyOwners;
        if (rootCause) {
            stateTimeline.rootCause = rootCause;
        }
        await IncidentEpisodeStateTimelineService.create({
            data: stateTimeline,
            props: props || {},
        });
        /*
         * Note: resolvedAt is updated by IncidentEpisodeStateTimelineService.onCreateSuccess()
         * to avoid duplicate updates.
         */
        // Cascade state change to all member incidents if requested
        if (cascadeToIncidents) {
            await this.cascadeStateToMemberIncidents({
                projectId,
                episodeId,
                incidentStateId,
                props,
            });
        }
    }
    async cascadeStateToMemberIncidents(data) {
        const { projectId, episodeId, incidentStateId, props } = data;
        // Get all member incidents for this episode
        const members = await IncidentEpisodeMemberService.findBy({
            query: {
                incidentEpisodeId: episodeId,
                projectId: projectId,
            },
            select: {
                incidentId: true,
            },
            props: {
                isRoot: true,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
        });
        if (members.length === 0) {
            return;
        }
        // Update state for each member incident
        for (const member of members) {
            if (!member.incidentId) {
                continue;
            }
            try {
                await IncidentService.changeIncidentState({
                    projectId: projectId,
                    incidentId: member.incidentId,
                    incidentStateId: incidentStateId,
                    shouldNotifyStatusPageSubscribers: false,
                    isSubscribersNotified: false,
                    notifyOwners: false, // Don't send notifications for cascaded state changes
                    rootCause: "State changed by episode state cascade.",
                    stateChangeLog: undefined,
                    props: props,
                });
            }
            catch (error) {
                logger.error(`Failed to cascade state change to incident ${member.incidentId.toString()}: ${error}`, {
                    projectId: projectId.toString(),
                    incidentEpisodeId: episodeId.toString(),
                    incidentId: member.incidentId.toString(),
                });
            }
        }
    }
    async acknowledgeEpisode(episodeId, acknowledgedByUserId, cascadeToIncidents = true) {
        const episode = await this.findOneById({
            id: episodeId,
            select: {
                projectId: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!episode || !episode.projectId) {
            throw new BadDataException("Episode not found.");
        }
        const incidentState = await IncidentStateService.findOneBy({
            query: {
                projectId: episode.projectId,
                isAcknowledgedState: true,
            },
            select: {
                _id: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!incidentState || !incidentState.id) {
            throw new BadDataException("Acknowledged incident state not found for this project.");
        }
        await this.changeEpisodeState({
            projectId: episode.projectId,
            episodeId: episodeId,
            incidentStateId: incidentState.id,
            notifyOwners: true,
            rootCause: acknowledgedByUserId
                ? `Acknowledged by user.`
                : "Acknowledged via API.",
            props: {
                isRoot: true,
                userId: acknowledgedByUserId,
            },
            cascadeToIncidents: cascadeToIncidents,
        });
    }
    async resolveEpisode(episodeId, resolvedByUserId, cascadeToIncidents = true) {
        const episode = await this.findOneById({
            id: episodeId,
            select: {
                projectId: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!episode || !episode.projectId) {
            throw new BadDataException("Episode not found.");
        }
        const incidentState = await IncidentStateService.findOneBy({
            query: {
                projectId: episode.projectId,
                isResolvedState: true,
            },
            select: {
                _id: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!incidentState || !incidentState.id) {
            throw new BadDataException("Resolved incident state not found for this project.");
        }
        await this.changeEpisodeState({
            projectId: episode.projectId,
            episodeId: episodeId,
            incidentStateId: incidentState.id,
            notifyOwners: true,
            rootCause: resolvedByUserId ? `Resolved by user.` : "Resolved via API.",
            props: {
                isRoot: true,
                userId: resolvedByUserId,
            },
            cascadeToIncidents: cascadeToIncidents,
        });
    }
    async reopenEpisode(episodeId, reopenedByUserId, cascadeToIncidents = true) {
        const episode = await this.findOneById({
            id: episodeId,
            select: {
                projectId: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!episode || !episode.projectId) {
            throw new BadDataException("Episode not found.");
        }
        const incidentState = await IncidentStateService.findOneBy({
            query: {
                projectId: episode.projectId,
                isCreatedState: true,
            },
            select: {
                _id: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!incidentState || !incidentState.id) {
            throw new BadDataException("Created incident state not found for this project.");
        }
        await this.changeEpisodeState({
            projectId: episode.projectId,
            episodeId: episodeId,
            incidentStateId: incidentState.id,
            notifyOwners: true,
            rootCause: reopenedByUserId ? `Reopened by user.` : "Reopened via API.",
            props: {
                isRoot: true,
                userId: reopenedByUserId,
            },
            cascadeToIncidents: cascadeToIncidents,
        });
        // Clear resolved timestamp and allIncidentsResolvedAt when episode is reopened
        await this.updateOneById({
            id: episodeId,
            data: {
                resolvedAt: null,
                allIncidentsResolvedAt: null,
            },
            props: {
                isRoot: true,
            },
        });
    }
    async updateEpisodeSeverity(episodeId, severityId, onlyIfHigher = false) {
        const episode = await this.findOneById({
            id: episodeId,
            select: {
                projectId: true,
                incidentSeverityId: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!episode || !episode.projectId) {
            throw new BadDataException("Episode not found.");
        }
        // If onlyIfHigher is true, check if the new severity is higher than the current
        if (onlyIfHigher && episode.incidentSeverityId) {
            const currentSeverity = await IncidentSeverityService.findOneById({
                id: episode.incidentSeverityId,
                select: {
                    order: true,
                },
                props: {
                    isRoot: true,
                },
            });
            const newSeverity = await IncidentSeverityService.findOneById({
                id: severityId,
                select: {
                    order: true,
                },
                props: {
                    isRoot: true,
                },
            });
            // Lower order number means higher severity
            if ((currentSeverity === null || currentSeverity === void 0 ? void 0 : currentSeverity.order) !== undefined &&
                (newSeverity === null || newSeverity === void 0 ? void 0 : newSeverity.order) !== undefined &&
                newSeverity.order >= currentSeverity.order) {
                return; // New severity is not higher, don't update
            }
        }
        await this.updateOneById({
            id: episodeId,
            data: {
                incidentSeverityId: severityId,
            },
            props: {
                isRoot: true,
            },
        });
        // Create feed entry for severity change
        const newSeverity = await IncidentSeverityService.findOneById({
            id: severityId,
            select: {
                name: true,
                color: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (newSeverity && episode.projectId) {
            await IncidentEpisodeFeedService.createIncidentEpisodeFeedItem({
                incidentEpisodeId: episodeId,
                projectId: episode.projectId,
                incidentEpisodeFeedEventType: IncidentEpisodeFeedEventType.SeverityChanged,
                displayColor: newSeverity.color || Yellow500,
                feedInfoInMarkdown: `Episode severity changed to **${newSeverity.name || "Unknown"}**`,
            });
        }
    }
    async updateIncidentCount(episodeId) {
        const count = await IncidentEpisodeMemberService.countBy({
            query: {
                incidentEpisodeId: episodeId,
            },
            props: {
                isRoot: true,
            },
        });
        const incidentCount = count.toNumber();
        // Get the episode to check for templates
        const episode = await this.findOneById({
            id: episodeId,
            select: {
                titleTemplate: true,
                descriptionTemplate: true,
                title: true,
                description: true,
            },
            props: {
                isRoot: true,
            },
        });
        const updateData = {
            incidentCount: incidentCount,
        };
        // Update title with dynamic variables if template exists
        if (episode === null || episode === void 0 ? void 0 : episode.titleTemplate) {
            updateData.title = this.renderTemplateWithDynamicValues(episode.titleTemplate, incidentCount);
        }
        // Update description with dynamic variables if template exists
        if (episode === null || episode === void 0 ? void 0 : episode.descriptionTemplate) {
            updateData.description = this.renderTemplateWithDynamicValues(episode.descriptionTemplate, incidentCount);
        }
        await this.updateOneById({
            id: episodeId,
            data: updateData,
            props: {
                isRoot: true,
            },
        });
    }
    renderTemplateWithDynamicValues(template, incidentCount) {
        let result = template;
        // Replace dynamic variables
        result = result.replace(/\{\{incidentCount\}\}/g, incidentCount.toString());
        return result;
    }
    async updateLastIncidentAddedAt(episodeId) {
        await this.updateOneById({
            id: episodeId,
            data: {
                lastIncidentAddedAt: OneUptimeDate.getCurrentDate(),
            },
            props: {
                isRoot: true,
            },
        });
    }
    async findOwners(episodeId) {
        // Get direct user owners
        const userOwners = await IncidentEpisodeOwnerUserService.findBy({
            query: {
                incidentEpisodeId: episodeId,
            },
            select: {
                userId: true,
                user: {
                    _id: true,
                    email: true,
                    name: true,
                },
            },
            props: {
                isRoot: true,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
        });
        // Get team owners
        const teamOwners = await IncidentEpisodeOwnerTeamService.findBy({
            query: {
                incidentEpisodeId: episodeId,
            },
            select: {
                teamId: true,
            },
            props: {
                isRoot: true,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
        });
        // Collect all unique users
        const usersMap = new Map();
        // Add direct user owners
        for (const owner of userOwners) {
            if (owner.user && owner.userId) {
                usersMap.set(owner.userId.toString(), owner.user);
            }
        }
        // Add users from teams
        for (const teamOwner of teamOwners) {
            if (teamOwner.teamId) {
                const teamMembers = await TeamMemberService.getUsersInTeam(teamOwner.teamId);
                for (const user of teamMembers) {
                    if (user.id) {
                        usersMap.set(user.id.toString(), user);
                    }
                }
            }
        }
        return Array.from(usersMap.values());
    }
    async addOwners(data) {
        const { episodeId, projectId, userIds, teamIds, createdByUserId } = data;
        // Add user owners
        if (userIds && userIds.length > 0) {
            for (const userId of userIds) {
                // Check if already exists
                const existing = await IncidentEpisodeOwnerUserService.findOneBy({
                    query: {
                        incidentEpisodeId: episodeId,
                        userId: userId,
                    },
                    props: {
                        isRoot: true,
                    },
                    select: {
                        _id: true,
                    },
                });
                if (!existing) {
                    const ownerUser = new IncidentEpisodeOwnerUser();
                    ownerUser.incidentEpisodeId = episodeId;
                    ownerUser.userId = userId;
                    ownerUser.projectId = projectId;
                    if (createdByUserId) {
                        ownerUser.createdByUserId = createdByUserId;
                    }
                    await IncidentEpisodeOwnerUserService.create({
                        data: ownerUser,
                        props: {
                            isRoot: true,
                        },
                    });
                }
            }
        }
        // Add team owners
        if (teamIds && teamIds.length > 0) {
            for (const teamId of teamIds) {
                // Check if already exists
                const existing = await IncidentEpisodeOwnerTeamService.findOneBy({
                    query: {
                        incidentEpisodeId: episodeId,
                        teamId: teamId,
                    },
                    props: {
                        isRoot: true,
                    },
                    select: {
                        _id: true,
                    },
                });
                if (!existing) {
                    const ownerTeam = new IncidentEpisodeOwnerTeam();
                    ownerTeam.incidentEpisodeId = episodeId;
                    ownerTeam.teamId = teamId;
                    ownerTeam.projectId = projectId;
                    if (createdByUserId) {
                        ownerTeam.createdByUserId = createdByUserId;
                    }
                    await IncidentEpisodeOwnerTeamService.create({
                        data: ownerTeam,
                        props: {
                            isRoot: true,
                        },
                    });
                }
            }
        }
    }
    async getWorkspaceChannelForEpisode(data) {
        const episode = await this.findOneById({
            id: data.episodeId,
            select: {
                postUpdatesToWorkspaceChannels: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!episode) {
            throw new BadDataException("Incident Episode not found.");
        }
        return (episode.postUpdatesToWorkspaceChannels || []).filter((channel) => {
            if (!data.workspaceType) {
                return true;
            }
            return channel.workspaceType === data.workspaceType;
        });
    }
    async isEpisodeResolved(episodeId) {
        var _a;
        const episode = await this.findOneById({
            id: episodeId,
            select: {
                projectId: true,
                currentIncidentState: {
                    order: true,
                },
            },
            props: {
                isRoot: true,
            },
        });
        if (!episode || !episode.projectId) {
            throw new BadDataException("Episode not found.");
        }
        const resolvedState = await IncidentStateService.getResolvedIncidentState({
            projectId: episode.projectId,
            props: {
                isRoot: true,
            },
        });
        const currentOrder = ((_a = episode.currentIncidentState) === null || _a === void 0 ? void 0 : _a.order) || 0;
        const resolvedOrder = resolvedState.order || 0;
        return currentOrder >= resolvedOrder;
    }
    async isEpisodeAcknowledged(data) {
        var _a;
        const episode = await this.findOneById({
            id: data.episodeId,
            select: {
                projectId: true,
                currentIncidentState: {
                    order: true,
                },
            },
            props: {
                isRoot: true,
            },
        });
        if (!episode || !episode.projectId) {
            throw new BadDataException("Episode not found.");
        }
        const acknowledgedState = await IncidentStateService.getAcknowledgedIncidentState({
            projectId: episode.projectId,
            props: {
                isRoot: true,
            },
        });
        const currentOrder = ((_a = episode.currentIncidentState) === null || _a === void 0 ? void 0 : _a.order) || 0;
        const acknowledgedOrder = acknowledgedState.order || 0;
        return currentOrder >= acknowledgedOrder;
    }
    async getEpisodeLinkInDashboard(projectId, episodeId) {
        const dashboardUrl = await DatabaseConfig.getDashboardUrl();
        return URL.fromString(dashboardUrl.toString()).addRoute(`/${projectId.toString()}/incidents/episodes/${episodeId.toString()}`);
    }
    async getEpisodeNumber(data) {
        const episode = await this.findOneById({
            id: data.episodeId,
            select: {
                episodeNumber: true,
                episodeNumberWithPrefix: true,
            },
            props: {
                isRoot: true,
            },
        });
        if (!episode) {
            throw new BadDataException("Episode not found.");
        }
        return {
            number: episode.episodeNumber ? Number(episode.episodeNumber) : null,
            numberWithPrefix: episode.episodeNumberWithPrefix || null,
        };
    }
}
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "onBeforeFind", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "countBy", 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]),
    __metadata("design:returntype", Promise)
], Service.prototype, "onBeforeDelete", 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", [Object, Model]),
    __metadata("design:returntype", Promise)
], Service.prototype, "onCreateSuccess", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Model]),
    __metadata("design:returntype", Promise)
], Service.prototype, "handleEpisodeWorkspaceOperationsAsync", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Model]),
    __metadata("design:returntype", Promise)
], Service.prototype, "createEpisodeCreatedFeed", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Model]),
    __metadata("design:returntype", Promise)
], Service.prototype, "executeEpisodeOnCallDutyPoliciesAsync", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "changeEpisodeState", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "cascadeStateToMemberIncidents", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID,
        ObjectID, Boolean]),
    __metadata("design:returntype", Promise)
], Service.prototype, "acknowledgeEpisode", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID,
        ObjectID, Boolean]),
    __metadata("design:returntype", Promise)
], Service.prototype, "resolveEpisode", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID,
        ObjectID, Boolean]),
    __metadata("design:returntype", Promise)
], Service.prototype, "reopenEpisode", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID,
        ObjectID, Boolean]),
    __metadata("design:returntype", Promise)
], Service.prototype, "updateEpisodeSeverity", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID]),
    __metadata("design:returntype", Promise)
], Service.prototype, "updateIncidentCount", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID]),
    __metadata("design:returntype", Promise)
], Service.prototype, "updateLastIncidentAddedAt", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID]),
    __metadata("design:returntype", Promise)
], Service.prototype, "findOwners", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "addOwners", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "getWorkspaceChannelForEpisode", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID]),
    __metadata("design:returntype", Promise)
], Service.prototype, "isEpisodeResolved", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "isEpisodeAcknowledged", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [ObjectID,
        ObjectID]),
    __metadata("design:returntype", Promise)
], Service.prototype, "getEpisodeLinkInDashboard", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "getEpisodeNumber", null);
export default new Service();
//# sourceMappingURL=IncidentEpisodeService.js.map