"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
    return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadCustomerNotificationMethods = loadCustomerNotificationMethods;
var simpl_schema_1 = require("simpl-schema");
var customer_notification_collection_1 = require("../collections/customer-notification.collection");
var user_collection_1 = require("../collections/user.collection");
var common_1 = require("../util/common");
var DEFAULT_LIST_LIMIT = 100;
var MAX_LIST_LIMIT = 500;
var DEDUPE_WINDOW_MS = 30 * 60 * 1000;
var MAX_TARGET_USERS = 5000;
function normalizeString(value, maxLength) {
    if (maxLength === void 0) { maxLength = 500; }
    var normalized = String(value || '').trim();
    if (!normalized) {
        return '';
    }
    if (normalized.length <= maxLength) {
        return normalized;
    }
    return normalized.slice(0, maxLength);
}
function normalizeNotificationCategory(value) {
    var normalized = normalizeString(value, 40).toLowerCase();
    if (!normalized) {
        return 'system';
    }
    if (['autofix', 'slow-query', 'system', 'manual'].includes(normalized)) {
        return normalized;
    }
    return 'system';
}
function normalizeNotificationSeverity(value) {
    var normalized = normalizeString(value, 20).toLowerCase();
    if (!normalized) {
        return 'info';
    }
    if (['info', 'success', 'warning', 'error'].includes(normalized)) {
        return normalized;
    }
    return 'info';
}
function normalizeListLimit(value) {
    var parsed = Number(value);
    if (!Number.isFinite(parsed) || parsed <= 0) {
        return DEFAULT_LIST_LIMIT;
    }
    return Math.min(Math.floor(parsed), MAX_LIST_LIMIT);
}
function isSystemMethodUser(methodUser) {
    return !methodUser || methodUser === 'Internal System';
}
function resolveMethodUser(methodUser, methodUserId) {
    return __awaiter(this, void 0, void 0, function () {
        var idUser;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    if (isSystemMethodUser(methodUser)) {
                        return [2 /*return*/, null];
                    }
                    if (typeof methodUser === 'object' && (methodUser === null || methodUser === void 0 ? void 0 : methodUser._id) && (methodUser === null || methodUser === void 0 ? void 0 : methodUser.roles)) {
                        return [2 /*return*/, methodUser];
                    }
                    idUser = normalizeString(typeof methodUserId === 'string'
                        ? methodUserId
                        : ((methodUserId === null || methodUserId === void 0 ? void 0 : methodUserId._id) ||
                            (methodUserId === null || methodUserId === void 0 ? void 0 : methodUserId.id_user) ||
                            (typeof methodUser === 'string'
                                ? methodUser
                                : ((methodUser === null || methodUser === void 0 ? void 0 : methodUser._id) || (methodUser === null || methodUser === void 0 ? void 0 : methodUser.id_user) || ''))), 80);
                    if (!idUser) {
                        return [2 /*return*/, null];
                    }
                    return [4 /*yield*/, user_collection_1.Users.findById(idUser)];
                case 1: return [2 /*return*/, _a.sent()];
            }
        });
    });
}
function isSuperAdmin(user) {
    var _a;
    return !!((_a = user === null || user === void 0 ? void 0 : user.roles) === null || _a === void 0 ? void 0 : _a.super_admin);
}
function uniqueUserIds(input) {
    var e_1, _a;
    var unique = new Set();
    try {
        for (var _b = __values((Array.isArray(input) ? input : [])), _c = _b.next(); !_c.done; _c = _b.next()) {
            var entry = _c.value;
            var id = normalizeString(entry, 80);
            if (id) {
                unique.add(id);
            }
            if (unique.size >= MAX_TARGET_USERS) {
                break;
            }
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
        }
        finally { if (e_1) throw e_1.error; }
    }
    return Array.from(unique);
}
function buildClientMembershipCriteria(idClient) {
    return {
        $or: [
            { 'other.id_client': idClient },
            { 'other.idClient': idClient },
            { id_client: idClient }
        ]
    };
}
function buildInternalUserCriteria() {
    return {
        $and: [
            {
                is_customer: {
                    $ne: true
                }
            },
            {
                $nor: [
                    {
                        'other.id_customer': {
                            $exists: true,
                            $nin: ['', null]
                        }
                    },
                    {
                        id_customer: {
                            $exists: true,
                            $nin: ['', null]
                        }
                    }
                ]
            }
        ]
    };
}
function buildAdminUserCriteria() {
    return {
        $or: [
            {
                'roles.super_admin': true
            },
            {
                'roles.groups.name': {
                    $regex: 'admin',
                    $options: 'i'
                }
            },
            {
                'roles.groups.views': {
                    $regex: '^/manage'
                }
            },
            {
                'roles.groups.views': '/manage'
            }
        ]
    };
}
function resolveTargetUsers(payload) {
    return __awaiter(this, void 0, void 0, function () {
        var targetType, users_1, idClient, andClauses, users;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    targetType = normalizeString((payload === null || payload === void 0 ? void 0 : payload.target_type) || 'client', 20).toLowerCase() || 'client';
                    if (targetType === 'users') {
                        return [2 /*return*/, uniqueUserIds((payload === null || payload === void 0 ? void 0 : payload.id_users) || [])];
                    }
                    if (!(targetType === 'all_active')) return [3 /*break*/, 2];
                    return [4 /*yield*/, user_collection_1.Users.find({
                            active: true,
                            username: {
                                $exists: true
                            }
                        }, {
                            projection: {
                                _id: 1
                            },
                            limit: MAX_TARGET_USERS
                        })];
                case 1:
                    users_1 = _a.sent();
                    return [2 /*return*/, uniqueUserIds(users_1.map(function (user) { return user === null || user === void 0 ? void 0 : user._id; }))];
                case 2:
                    idClient = normalizeString(payload === null || payload === void 0 ? void 0 : payload.id_client, 80);
                    if (!idClient) {
                        return [2 /*return*/, []];
                    }
                    andClauses = [
                        {
                            active: true
                        },
                        {
                            username: {
                                $exists: true
                            }
                        },
                        buildClientMembershipCriteria(idClient)
                    ];
                    if (targetType === 'client_internal' || targetType === 'client_admins') {
                        andClauses.push(buildInternalUserCriteria());
                    }
                    if (targetType === 'client_admins') {
                        andClauses.push(buildAdminUserCriteria());
                    }
                    return [4 /*yield*/, user_collection_1.Users.find({
                            $and: andClauses
                        }, {
                            projection: {
                                _id: 1
                            },
                            limit: MAX_TARGET_USERS
                        })];
                case 3:
                    users = _a.sent();
                    return [2 /*return*/, uniqueUserIds(users.map(function (user) { return user === null || user === void 0 ? void 0 : user._id; }))];
            }
        });
    });
}
function loadCustomerNotificationMethods(methodManager) {
    methodManager.methods({
        createCustomerNotification: {
            check: new simpl_schema_1.default({
                payload: {
                    type: Object,
                    blackbox: true
                }
            }),
            function: function (payload) {
                return __awaiter(this, void 0, void 0, function () {
                    var methodUser, systemUser, title, message, idClient, clientName, category, severity, details, source, sourceId, actionLabel, actionRoute, dedupeKey, expiresAt, metadata, targetUsers, now, createById, createByName, skippedByDedupe, cutoff, existing, docs;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0: return [4 /*yield*/, resolveMethodUser(this.user, this.id_user)];
                            case 1:
                                methodUser = _a.sent();
                                systemUser = isSystemMethodUser(this.user);
                                if (!systemUser && !isSuperAdmin(methodUser)) {
                                    throw new Error('Only super admins can create notifications.');
                                }
                                title = normalizeString(payload === null || payload === void 0 ? void 0 : payload.title, 160);
                                message = normalizeString(payload === null || payload === void 0 ? void 0 : payload.message, 1500);
                                if (!title || !message) {
                                    throw new Error('Notification title and message are required.');
                                }
                                idClient = normalizeString(payload === null || payload === void 0 ? void 0 : payload.id_client, 80);
                                clientName = normalizeString(payload === null || payload === void 0 ? void 0 : payload.client_name, 200);
                                category = normalizeNotificationCategory(payload === null || payload === void 0 ? void 0 : payload.category);
                                severity = normalizeNotificationSeverity(payload === null || payload === void 0 ? void 0 : payload.severity);
                                details = normalizeString(payload === null || payload === void 0 ? void 0 : payload.details, 6000);
                                source = normalizeString(payload === null || payload === void 0 ? void 0 : payload.source, 120);
                                sourceId = normalizeString(payload === null || payload === void 0 ? void 0 : payload.source_id, 120);
                                actionLabel = normalizeString(payload === null || payload === void 0 ? void 0 : payload.action_label, 80);
                                actionRoute = normalizeString(payload === null || payload === void 0 ? void 0 : payload.action_route, 500);
                                dedupeKey = normalizeString(payload === null || payload === void 0 ? void 0 : payload.dedupe_key, 160);
                                expiresAt = (payload === null || payload === void 0 ? void 0 : payload.expires_at) instanceof Date && !Number.isNaN(payload.expires_at.getTime())
                                    ? payload.expires_at
                                    : undefined;
                                metadata = (payload === null || payload === void 0 ? void 0 : payload.metadata) && typeof payload.metadata === 'object'
                                    ? payload.metadata
                                    : undefined;
                                return [4 /*yield*/, resolveTargetUsers(payload)];
                            case 2:
                                targetUsers = _a.sent();
                                if (!targetUsers.length) {
                                    return [2 /*return*/, {
                                            created: 0,
                                            reason: 'no_target_users'
                                        }];
                                }
                                now = new Date();
                                createById = systemUser ? 'Internal System' : normalizeString(methodUser === null || methodUser === void 0 ? void 0 : methodUser._id, 80);
                                createByName = systemUser
                                    ? 'Internal System'
                                    : normalizeString((methodUser === null || methodUser === void 0 ? void 0 : methodUser.fullname) || (methodUser === null || methodUser === void 0 ? void 0 : methodUser.username), 200);
                                skippedByDedupe = new Set();
                                if (!dedupeKey) return [3 /*break*/, 4];
                                cutoff = new Date(now.getTime() - DEDUPE_WINDOW_MS);
                                return [4 /*yield*/, customer_notification_collection_1.CustomerNotifications.find({
                                        id_user: {
                                            $in: targetUsers
                                        },
                                        dedupe_key: dedupeKey,
                                        createdAt: {
                                            $gte: cutoff
                                        }
                                    }, {
                                        projection: {
                                            id_user: 1
                                        },
                                        limit: MAX_TARGET_USERS
                                    })];
                            case 3:
                                existing = _a.sent();
                                skippedByDedupe = new Set(existing.map(function (entry) { return normalizeString(entry === null || entry === void 0 ? void 0 : entry.id_user, 80); }).filter(Boolean));
                                _a.label = 4;
                            case 4:
                                docs = targetUsers
                                    .filter(function (idUser) { return !skippedByDedupe.has(idUser); })
                                    .map(function (idUser) { return ({
                                    _id: (0, common_1.objectIdHexString)(),
                                    __v: 0,
                                    id_user: idUser,
                                    id_client: idClient || undefined,
                                    client_name: clientName || undefined,
                                    title: title,
                                    message: message,
                                    details: details || undefined,
                                    category: category,
                                    severity: severity,
                                    status: 'unread',
                                    read_at: undefined,
                                    read_by: '',
                                    source: source || undefined,
                                    source_id: sourceId || undefined,
                                    action_label: actionLabel || undefined,
                                    action_route: actionRoute || undefined,
                                    dedupe_key: dedupeKey || undefined,
                                    metadata: metadata,
                                    expires_at: expiresAt,
                                    created_by: createById || undefined,
                                    created_by_name: createByName || undefined,
                                    createdAt: now,
                                    updatedAt: now
                                }); });
                                if (!docs.length) {
                                    return [2 /*return*/, {
                                            created: 0,
                                            reason: 'dedupe_suppressed'
                                        }];
                                }
                                return [4 /*yield*/, customer_notification_collection_1.CustomerNotifications.insertMany(docs)];
                            case 5:
                                _a.sent();
                                return [2 /*return*/, {
                                        created: docs.length,
                                        targeted: targetUsers.length
                                    }];
                        }
                    });
                });
            }
        },
        markUserNotificationRead: {
            check: new simpl_schema_1.default({
                id_notification: {
                    type: String
                }
            }),
            function: function (id_notification) {
                return __awaiter(this, void 0, void 0, function () {
                    var methodUser, idUser;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0: return [4 /*yield*/, resolveMethodUser(this.user, this.id_user)];
                            case 1:
                                methodUser = _a.sent();
                                idUser = normalizeString(methodUser === null || methodUser === void 0 ? void 0 : methodUser._id, 80);
                                if (!idUser) {
                                    throw new Error('You must be logged in to mark notifications as read.');
                                }
                                return [4 /*yield*/, customer_notification_collection_1.CustomerNotifications.updateOne({
                                        _id: id_notification,
                                        id_user: idUser
                                    }, {
                                        $set: {
                                            status: 'read',
                                            read_at: new Date(),
                                            read_by: idUser
                                        }
                                    })];
                            case 2:
                                _a.sent();
                                return [2 /*return*/, true];
                        }
                    });
                });
            }
        },
        markAllUserNotificationsRead: {
            check: new simpl_schema_1.default({
                id_client: {
                    type: String,
                    optional: true
                }
            }),
            function: function (id_client) {
                return __awaiter(this, void 0, void 0, function () {
                    var methodUser, idUser, idClient, query;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0: return [4 /*yield*/, resolveMethodUser(this.user, this.id_user)];
                            case 1:
                                methodUser = _a.sent();
                                idUser = normalizeString(methodUser === null || methodUser === void 0 ? void 0 : methodUser._id, 80);
                                if (!idUser) {
                                    throw new Error('You must be logged in to mark notifications as read.');
                                }
                                idClient = normalizeString(id_client, 80);
                                query = {
                                    id_user: idUser,
                                    status: 'unread'
                                };
                                if (idClient) {
                                    query.id_client = idClient;
                                }
                                return [4 /*yield*/, customer_notification_collection_1.CustomerNotifications.updateMany(query, {
                                        $set: {
                                            status: 'read',
                                            read_at: new Date(),
                                            read_by: idUser
                                        }
                                    })];
                            case 2:
                                _a.sent();
                                return [2 /*return*/, true];
                        }
                    });
                });
            }
        },
        listCustomerNotifications: {
            check: new simpl_schema_1.default({
                payload: {
                    type: Object,
                    optional: true,
                    blackbox: true
                }
            }),
            function: function (payload) {
                return __awaiter(this, void 0, void 0, function () {
                    var methodUser, systemUser, query, idUser, idClient, status, limit;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0: return [4 /*yield*/, resolveMethodUser(this.user, this.id_user)];
                            case 1:
                                methodUser = _a.sent();
                                systemUser = isSystemMethodUser(this.user);
                                if (!systemUser && !isSuperAdmin(methodUser)) {
                                    throw new Error('Only super admins can list all notifications.');
                                }
                                query = {};
                                idUser = normalizeString(payload === null || payload === void 0 ? void 0 : payload.id_user, 80);
                                idClient = normalizeString(payload === null || payload === void 0 ? void 0 : payload.id_client, 80);
                                status = normalizeString(payload === null || payload === void 0 ? void 0 : payload.status, 20).toLowerCase();
                                if (idUser) {
                                    query.id_user = idUser;
                                }
                                if (idClient) {
                                    query.id_client = idClient;
                                }
                                if (status === 'unread' || status === 'read') {
                                    query.status = status;
                                }
                                limit = normalizeListLimit(payload === null || payload === void 0 ? void 0 : payload.limit);
                                return [4 /*yield*/, customer_notification_collection_1.CustomerNotifications.find(query, {
                                        sort: {
                                            createdAt: -1
                                        },
                                        limit: limit
                                    })];
                            case 2: return [2 /*return*/, _a.sent()];
                        }
                    });
                });
            }
        }
    });
}

//# sourceMappingURL=customer-notifications.js.map
