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 MonitorService from "./MonitorService";
import BadDataException from "../../Types/Exception/BadDataException";
import LIMIT_MAX from "../../Types/Database/LimitMax";
import Model from "../../Models/DatabaseModels/MonitorTemplate";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
const ALL_SYNCABLE_FIELDS = [
    "monitorSteps",
    "monitoringInterval",
    "minimumProbeAgreement",
    "labels",
];
export class Service extends DatabaseService {
    constructor() {
        super(Model);
    }
    /**
     * Count monitors created from this template.
     * Caller must already have read access on the template via the API layer.
     */
    async countLinkedMonitors(data) {
        const count = await MonitorService.countBy({
            query: {
                monitorTemplateId: data.monitorTemplateId,
                projectId: data.projectId,
            },
            props: {
                isRoot: true,
            },
        });
        return count.toNumber();
    }
    /**
     * Validate and narrow a list of field names to the syncable subset.
     * Anything not in the whitelist throws — we never silently drop a field the
     * caller asked for, that would mask UI bugs.
     */
    validateSyncableFields(fields) {
        if (!fields || fields.length === 0) {
            return [...ALL_SYNCABLE_FIELDS];
        }
        const allowed = new Set(ALL_SYNCABLE_FIELDS);
        for (const field of fields) {
            if (!allowed.has(field)) {
                throw new BadDataException(`Field "${field}" is not syncable from a monitor template`);
            }
        }
        return fields;
    }
    buildUpdateData(template, fields) {
        const updateData = {};
        for (const field of fields) {
            const value = template[field];
            if (value === undefined) {
                continue;
            }
            updateData[field] = value;
        }
        return updateData;
    }
    /**
     * Push the template's current configuration onto every monitor that was
     * created from it. Sync is intentionally explicit (button-triggered) so a
     * config tweak doesn't silently re-deploy across the whole fleet.
     *
     * Pass `fields` to scope the sync — e.g. `["monitorSteps"]` to push only the
     * criteria. If omitted, every syncable field is pushed.
     */
    async syncLinkedMonitors(data) {
        const fields = this.validateSyncableFields(data.fields);
        const template = await this.findOneById({
            id: data.monitorTemplateId,
            select: {
                _id: true,
                projectId: true,
                monitorSteps: true,
                monitoringInterval: true,
                minimumProbeAgreement: true,
                labels: {
                    _id: true,
                },
            },
            props: data.props,
        });
        if (!template) {
            throw new BadDataException("Monitor template not found");
        }
        if (!template.projectId) {
            throw new BadDataException("Monitor template is missing projectId");
        }
        const totalLinkedMonitors = await this.countLinkedMonitors({
            monitorTemplateId: template.id,
            projectId: template.projectId,
        });
        if (totalLinkedMonitors === 0) {
            return {
                totalLinkedMonitors: 0,
                syncedMonitors: 0,
            };
        }
        const updateData = this.buildUpdateData(template, fields);
        if (Object.keys(updateData).length === 0) {
            return {
                totalLinkedMonitors,
                syncedMonitors: 0,
            };
        }
        const syncedMonitors = await MonitorService.updateBy({
            query: {
                monitorTemplateId: template.id,
                projectId: template.projectId,
            },
            data: updateData,
            limit: LIMIT_MAX,
            skip: 0,
            props: data.props,
        });
        return {
            totalLinkedMonitors,
            syncedMonitors,
        };
    }
    /**
     * Sync the template's current configuration onto a single monitor that was
     * created from it. The monitor must be linked to this template — passing an
     * arbitrary monitor ID is rejected so the endpoint can't be tricked into
     * pushing config to an unrelated monitor.
     *
     * Pass `fields` to scope the sync; if omitted, every syncable field is
     * pushed.
     */
    async syncToMonitor(data) {
        const fields = this.validateSyncableFields(data.fields);
        const template = await this.findOneById({
            id: data.monitorTemplateId,
            select: {
                _id: true,
                projectId: true,
                monitorSteps: true,
                monitoringInterval: true,
                minimumProbeAgreement: true,
                labels: {
                    _id: true,
                },
            },
            props: data.props,
        });
        if (!template) {
            throw new BadDataException("Monitor template not found");
        }
        if (!template.projectId) {
            throw new BadDataException("Monitor template is missing projectId");
        }
        const monitor = await MonitorService.findOneById({
            id: data.monitorId,
            select: {
                _id: true,
                projectId: true,
                monitorTemplateId: true,
            },
            props: { isRoot: true },
        });
        if (!monitor) {
            throw new BadDataException("Monitor not found");
        }
        if (!monitor.monitorTemplateId ||
            monitor.monitorTemplateId.toString() !== template.id.toString()) {
            throw new BadDataException("Monitor is not linked to this template");
        }
        if (!monitor.projectId ||
            monitor.projectId.toString() !== template.projectId.toString()) {
            throw new BadDataException("Monitor and template belong to different projects");
        }
        const updateData = this.buildUpdateData(template, fields);
        if (Object.keys(updateData).length === 0) {
            return;
        }
        await MonitorService.updateOneById({
            id: data.monitorId,
            data: updateData,
            props: data.props,
        });
    }
    /**
     * Link an existing monitor to this template. The monitor must be in the same
     * project AND have the same monitorType as the template — anything else is
     * rejected, so a user can't (e.g.) link an API monitor to a Server-monitor
     * template and then sync incompatible criteria onto it.
     */
    async linkMonitor(data) {
        const template = await this.findOneById({
            id: data.monitorTemplateId,
            select: {
                _id: true,
                projectId: true,
                monitorType: true,
            },
            props: data.props,
        });
        if (!template) {
            throw new BadDataException("Monitor template not found");
        }
        if (!template.projectId) {
            throw new BadDataException("Monitor template is missing projectId");
        }
        if (!template.monitorType) {
            throw new BadDataException("Monitor template is missing monitorType");
        }
        const monitor = await MonitorService.findOneById({
            id: data.monitorId,
            select: {
                _id: true,
                projectId: true,
                monitorType: true,
            },
            props: data.props,
        });
        if (!monitor) {
            throw new BadDataException("Monitor not found");
        }
        if (!monitor.projectId ||
            monitor.projectId.toString() !== template.projectId.toString()) {
            throw new BadDataException("Monitor and template belong to different projects");
        }
        if (monitor.monitorType !== template.monitorType) {
            throw new BadDataException(`Monitor type "${monitor.monitorType}" does not match template type "${template.monitorType}"`);
        }
        await MonitorService.updateOneById({
            id: data.monitorId,
            data: {
                monitorTemplateId: template.id,
            },
            props: data.props,
        });
    }
    /**
     * Detach a monitor from this template. The monitor must currently be linked
     * to *this* template — passing a monitor linked elsewhere (or unlinked) is
     * rejected so a stale UI can't accidentally clear someone else's link.
     */
    async unlinkMonitor(data) {
        const monitor = await MonitorService.findOneById({
            id: data.monitorId,
            select: {
                _id: true,
                monitorTemplateId: true,
            },
            props: data.props,
        });
        if (!monitor) {
            throw new BadDataException("Monitor not found");
        }
        if (!monitor.monitorTemplateId ||
            monitor.monitorTemplateId.toString() !== data.monitorTemplateId.toString()) {
            throw new BadDataException("Monitor is not linked to this template");
        }
        await MonitorService.updateOneById({
            id: data.monitorId,
            data: {
                monitorTemplateId: null,
            },
            props: data.props,
        });
    }
}
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "countLinkedMonitors", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "syncLinkedMonitors", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "syncToMonitor", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "linkMonitor", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], Service.prototype, "unlinkMonitor", null);
export default new Service();
//# sourceMappingURL=MonitorTemplateService.js.map