"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
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 __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
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.");
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadMongoExplorerMethods = loadMongoExplorerMethods;
var simpl_schema_1 = require("simpl-schema");
var user_collection_1 = require("../collections/user.collection");
var openai_usage_ledger_manager_1 = require("../managers/openai-usage-ledger.manager");
var resolveio_server_app_1 = require("../resolveio-server-app");
var codex_client_1 = require("../services/codex-client");
var common_1 = require("../util/common");
var tokenizer_1 = require("../util/tokenizer");
var DEFAULT_LIMIT = 100;
var MAX_LIMIT = 2000;
var ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
var MAX_REVIEW_STRING_LENGTH = 500;
var MAX_REVIEW_ARRAY_ITEMS = 10;
var MAX_REVIEW_OBJECT_KEYS = 40;
var MAX_REVIEW_DEPTH = 4;
var MAX_REVIEW_LIST_ITEMS = 6;
var MAX_REVIEW_CHANGED_FIELDS = 60;
var DEFAULT_RISK_REVIEW_TIMEOUT_MS = 30000;
var DEFAULT_RISK_REVIEW_MAX_TOKENS = 700;
var SENSITIVE_REVIEW_FIELD_REGEX = /(password|secret|token|api[_-]?key|salt|hash|email|phone|address|ssn|services|roles)/i;
var DEFAULT_AI_SUGGEST_TIMEOUT_MS = 60000;
var DEFAULT_AI_SUGGEST_MAX_TOKENS = 1000;
var MAX_AI_SUGGEST_FIELDS = 700;
var MAX_AI_SUGGEST_COLLECTIONS = 800;
var MAX_AI_SUGGEST_RESULTS = 500;
var RESTRICTED_AGGREGATE_STAGES = new Set(['$out', '$merge']);
function parseMongoExplorerWriteUsers() {
    var raw = typeof process.env.MONGO_EXPLORER_WRITE_USERS === 'string' ? process.env.MONGO_EXPLORER_WRITE_USERS.trim() : '';
    if (!raw) {
        return [];
    }
    try {
        var parsed = JSON.parse(raw);
        if (Array.isArray(parsed)) {
            return parsed.map(function (value) { return String(value); }).filter(function (value) { return value.trim().length; });
        }
        if (typeof parsed === 'string') {
            return [parsed.trim()].filter(Boolean);
        }
    }
    catch (_a) {
        // Fall back to comma-separated list.
    }
    return raw
        .split(',')
        .map(function (value) { return value.trim(); })
        .filter(Boolean);
}
function normalizeExplorerMode(mode) {
    var normalizedMode = String(mode || '').trim().toLowerCase();
    if (normalizedMode === 'resolveio') {
        return 'resolveio';
    }
    return 'aicoder';
}
function allowUnschemaizedWrites() {
    return process.env.MONGO_EXPLORER_ALLOW_UNSCHEMATIZED_WRITE === 'true';
}
function resolveDatabaseName(database) {
    var _a, _b;
    var defaultDb = ((_a = resolveio_server_app_1.ResolveIOServer.getServerConfig()) === null || _a === void 0 ? void 0 : _a.DATABASE) || '';
    var dbName = typeof database === 'string' && database.trim().length ? database.trim() : defaultDb;
    if (!dbName) {
        throw new Error('Mongo Explorer: Database is required');
    }
    var allowedDatabases = ((_b = resolveio_server_app_1.ResolveIOServer.getMongoManager()) === null || _b === void 0 ? void 0 : _b.getWatchedDatabases()) || [];
    if (allowedDatabases.length && !allowedDatabases.includes(dbName)) {
        throw new Error('Mongo Explorer: Database access denied');
    }
    return dbName;
}
function resolveDatabase(database) {
    var dbName = resolveDatabaseName(database);
    return resolveio_server_app_1.ResolveIOServer.getMongoConnection().db(dbName);
}
function resolveCollectionHandle(database, collection) {
    var _a, _b;
    var mainDb = ((_a = resolveio_server_app_1.ResolveIOServer.getServerConfig()) === null || _a === void 0 ? void 0 : _a.DATABASE) || '';
    var isMainDb = database === mainDb;
    var managerCollection = isMainDb ? (_b = resolveio_server_app_1.ResolveIOServer.getMongoManager()) === null || _b === void 0 ? void 0 : _b.collection(collection) : null;
    var db = resolveio_server_app_1.ResolveIOServer.getMongoConnection().db(database);
    return {
        managerCollection: managerCollection,
        dbCollection: db.collection(collection)
    };
}
function getSchemaDefinition(collectionRef) {
    if (!collectionRef || !collectionRef.simplschema || typeof collectionRef.simplschema.schema !== 'function') {
        return {};
    }
    var schema = collectionRef.simplschema.schema();
    return schema && typeof schema === 'object' ? schema : {};
}
function getSchemaTypeName(definition) {
    var _a, _b;
    var typeDefs = (_a = definition === null || definition === void 0 ? void 0 : definition.type) === null || _a === void 0 ? void 0 : _a.definitions;
    if (!Array.isArray(typeDefs) || !typeDefs.length) {
        return 'Any';
    }
    var firstType = (_b = typeDefs[0]) === null || _b === void 0 ? void 0 : _b.type;
    if (!firstType) {
        return 'Any';
    }
    if (firstType === String) {
        return 'String';
    }
    if (firstType === Number) {
        return 'Number';
    }
    if (firstType === Boolean) {
        return 'Boolean';
    }
    if (firstType === Date) {
        return 'Date';
    }
    if (firstType === Object) {
        return 'Object';
    }
    if (firstType === Array) {
        return 'Array';
    }
    var typeName = firstType.name || String(firstType);
    if (typeName === 'Integer') {
        return 'Number';
    }
    return typeName;
}
function toLabel(path) {
    var last = path.split('.').pop() || path;
    return last
        .replace(/\$/g, '')
        .replace(/_/g, ' ')
        .replace(/\s+/g, ' ')
        .trim();
}
function isProtectedFieldSegment(segment) {
    if (!segment) {
        return false;
    }
    if (segment === '_id') {
        return true;
    }
    var normalizedSegment = segment.replace(/\[\d+\]/g, '');
    return normalizedSegment.toLowerCase().startsWith('id_');
}
function isProtectedFieldPath(path) {
    var segments = String(path || '').split('.').filter(Boolean);
    return segments.some(function (segment) { return isProtectedFieldSegment(segment); });
}
function safeStringify(value) {
    try {
        return JSON.stringify(value);
    }
    catch (_a) {
        return String(value);
    }
}
function collectProtectedFieldPaths(value, prefix, out) {
    if (prefix === void 0) { prefix = ''; }
    if (out === void 0) { out = {}; }
    if (Array.isArray(value)) {
        value.forEach(function (item, index) {
            var nextPrefix = prefix ? "".concat(prefix, "[").concat(index, "]") : "[".concat(index, "]");
            collectProtectedFieldPaths(item, nextPrefix, out);
        });
        return out;
    }
    if (!value || typeof value !== 'object') {
        return out;
    }
    Object.keys(value).forEach(function (key) {
        var nextPrefix = prefix ? "".concat(prefix, ".").concat(key) : key;
        if (isProtectedFieldSegment(key)) {
            out[nextPrefix] = value[key];
        }
        collectProtectedFieldPaths(value[key], nextPrefix, out);
    });
    return out;
}
function ensureProtectedFieldsUnchanged(originalDoc, updatedDoc) {
    var originalProtected = collectProtectedFieldPaths(originalDoc || {});
    var updatedProtected = collectProtectedFieldPaths(updatedDoc || {});
    var violations = [];
    Object.keys(originalProtected).forEach(function (path) {
        if (!Object.prototype.hasOwnProperty.call(updatedProtected, path)) {
            violations.push(path);
            return;
        }
        var originalValue = safeStringify(originalProtected[path]);
        var updatedValue = safeStringify(updatedProtected[path]);
        if (originalValue !== updatedValue) {
            violations.push(path);
        }
    });
    Object.keys(updatedProtected).forEach(function (path) {
        if (!Object.prototype.hasOwnProperty.call(originalProtected, path)) {
            violations.push(path);
        }
    });
    if (violations.length) {
        var preview = violations.slice(0, 4).join(', ');
        throw new Error("Mongo Explorer: Protected fields cannot be edited (".concat(preview).concat(violations.length > 4 ? ', ...' : '', ")"));
    }
}
function coerceDateValue(value) {
    if (value instanceof Date) {
        return value;
    }
    if (typeof value !== 'string' || !ISO_DATE_REGEX.test(value)) {
        return value;
    }
    var parsed = new Date(value);
    return Number.isNaN(parsed.getTime()) ? value : parsed;
}
function coerceDatePath(target, segments) {
    if (!target || !segments.length) {
        return;
    }
    var _a = __read(segments), segment = _a[0], rest = _a.slice(1);
    if (segment === '$') {
        if (Array.isArray(target)) {
            target.forEach(function (item) { return coerceDatePath(item, rest); });
        }
        return;
    }
    if (!Object.prototype.hasOwnProperty.call(target, segment)) {
        return;
    }
    if (rest.length === 0) {
        var updatedValue = coerceDateValue(target[segment]);
        if (updatedValue !== target[segment]) {
            target[segment] = updatedValue;
        }
        return;
    }
    coerceDatePath(target[segment], rest);
}
function coerceDateFields(collectionRef, doc) {
    if (!collectionRef || !collectionRef.simplschema || !doc) {
        return;
    }
    var schema = getSchemaDefinition(collectionRef);
    Object.keys(schema).forEach(function (schemaKey) {
        var _a;
        var definition = schema[schemaKey];
        var typeDefs = (_a = definition === null || definition === void 0 ? void 0 : definition.type) === null || _a === void 0 ? void 0 : _a.definitions;
        if (!Array.isArray(typeDefs) || !typeDefs.some(function (typeDef) { return typeDef.type === Date; })) {
            return;
        }
        coerceDatePath(doc, schemaKey.split('.'));
    });
}
function normalizeFindOptions(options) {
    var normalized = options || {};
    var projection = normalized.projection && Object.keys(normalized.projection).length ? normalized.projection : undefined;
    var sort = normalized.sort && Object.keys(normalized.sort).length ? normalized.sort : undefined;
    var limit = typeof normalized.limit === 'number' ? Math.min(Math.max(normalized.limit, 0), MAX_LIMIT) : DEFAULT_LIMIT;
    var skip = typeof normalized.skip === 'number' ? Math.max(normalized.skip, 0) : 0;
    return {
        findOptions: {
            projection: projection,
            sort: sort,
            limit: limit,
            skip: skip
        },
        includeTotal: normalized.includeTotal === true
    };
}
function userHasView(user, view) {
    var _a, _b, _c;
    if (!user || !view) {
        return false;
    }
    if ((_a = user.roles) === null || _a === void 0 ? void 0 : _a.super_admin) {
        return true;
    }
    var groups = Array.isArray((_b = user.roles) === null || _b === void 0 ? void 0 : _b.groups) ? user.roles.groups : [];
    var miscs = Array.isArray((_c = user.roles) === null || _c === void 0 ? void 0 : _c.miscs) ? user.roles.miscs : [];
    if (groups.some(function (group) { return Array.isArray(group.views) && group.views.some(function (v) { return v.startsWith(view); }); })) {
        return true;
    }
    if (miscs.some(function (v) { return v.startsWith(view); })) {
        return true;
    }
    if (groups.some(function (group) { return group.name === view; })) {
        return true;
    }
    return false;
}
function ensureWriteAccess(context, permissionView, mode) {
    return __awaiter(this, void 0, void 0, function () {
        var idUser, user, normalizedMode, allowedUsers, username_1, email_1, id_1, isAllowed, normalizedPermission;
        var _a;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    idUser = context === null || context === void 0 ? void 0 : context.id_user;
                    if (!idUser) {
                        throw new Error('Mongo Explorer: Unauthorized');
                    }
                    return [4 /*yield*/, user_collection_1.Users.findOne({ _id: idUser })];
                case 1:
                    user = _b.sent();
                    if (!user) {
                        throw new Error('Mongo Explorer: Unauthorized');
                    }
                    if (user.readonly) {
                        throw new Error('Mongo Explorer: Readonly user');
                    }
                    normalizedMode = normalizeExplorerMode(mode);
                    if (normalizedMode === 'resolveio') {
                        throw new Error('Mongo Explorer: ResolveIO mode is read only');
                    }
                    allowedUsers = parseMongoExplorerWriteUsers();
                    if (allowedUsers.length) {
                        username_1 = String(user.username || '').trim().toLowerCase();
                        email_1 = String(user.email || '').trim().toLowerCase();
                        id_1 = String(user._id || '').trim();
                        isAllowed = allowedUsers.some(function (entry) {
                            var normalized = String(entry || '').trim().toLowerCase();
                            if (!normalized) {
                                return false;
                            }
                            return normalized === username_1 || (email_1 && normalized === email_1) || (id_1 && entry === id_1);
                        });
                        if (!isAllowed) {
                            throw new Error('Mongo Explorer: Write access denied');
                        }
                    }
                    if ((_a = user.roles) === null || _a === void 0 ? void 0 : _a.super_admin) {
                        return [2 /*return*/];
                    }
                    normalizedPermission = typeof permissionView === 'string' ? permissionView.trim() : '';
                    if (!normalizedPermission) {
                        return [2 /*return*/];
                    }
                    if (userHasView(user, normalizedPermission)) {
                        return [2 /*return*/];
                    }
                    throw new Error('Mongo Explorer: Write access denied');
            }
        });
    });
}
function buildDeleteSafetyCollections(collectionRef) {
    var schema = getSchemaDefinition(collectionRef);
    return Object.keys(schema).filter(function (path) { return isProtectedFieldPath(path) && path !== '_id'; });
}
function buildDeleteImpact(database, sourceCollection, docId) {
    return __awaiter(this, void 0, void 0, function () {
        var mainDb, mongoManager, collections, impacts, unresolved, collections_1, collections_1_1, collectionRef, collectionName, referencePaths, query, docs, _a, e_1_1;
        var e_1, _b;
        var _c;
        return __generator(this, function (_d) {
            switch (_d.label) {
                case 0:
                    mainDb = ((_c = resolveio_server_app_1.ResolveIOServer.getServerConfig()) === null || _c === void 0 ? void 0 : _c.DATABASE) || '';
                    if (database !== mainDb) {
                        return [2 /*return*/, {
                                has_references: false,
                                unresolved: true,
                                collections: []
                            }];
                    }
                    mongoManager = resolveio_server_app_1.ResolveIOServer.getMongoManager();
                    collections = (mongoManager === null || mongoManager === void 0 ? void 0 : mongoManager.collections) ? mongoManager.collections() : [];
                    impacts = [];
                    unresolved = false;
                    _d.label = 1;
                case 1:
                    _d.trys.push([1, 8, 9, 10]);
                    collections_1 = __values(collections), collections_1_1 = collections_1.next();
                    _d.label = 2;
                case 2:
                    if (!!collections_1_1.done) return [3 /*break*/, 7];
                    collectionRef = collections_1_1.value;
                    collectionName = collectionRef === null || collectionRef === void 0 ? void 0 : collectionRef.collectionName;
                    if (!collectionName) {
                        return [3 /*break*/, 6];
                    }
                    if (collectionName === "".concat(sourceCollection, ".versions")) {
                        return [3 /*break*/, 6];
                    }
                    referencePaths = buildDeleteSafetyCollections(collectionRef);
                    if (!referencePaths.length) {
                        return [3 /*break*/, 6];
                    }
                    query = {
                        $or: referencePaths.map(function (path) {
                            var _a;
                            return (_a = {}, _a[path] = docId, _a);
                        })
                    };
                    _d.label = 3;
                case 3:
                    _d.trys.push([3, 5, , 6]);
                    return [4 /*yield*/, collectionRef.find(query, { projection: { _id: 1 }, limit: 3 })];
                case 4:
                    docs = _d.sent();
                    if (Array.isArray(docs) && docs.length) {
                        impacts.push({
                            collection: collectionName,
                            references: docs.length,
                            sample_ids: docs.map(function (doc) { return String((doc === null || doc === void 0 ? void 0 : doc._id) || ''); }).filter(Boolean)
                        });
                    }
                    return [3 /*break*/, 6];
                case 5:
                    _a = _d.sent();
                    unresolved = true;
                    return [3 /*break*/, 6];
                case 6:
                    collections_1_1 = collections_1.next();
                    return [3 /*break*/, 2];
                case 7: return [3 /*break*/, 10];
                case 8:
                    e_1_1 = _d.sent();
                    e_1 = { error: e_1_1 };
                    return [3 /*break*/, 10];
                case 9:
                    try {
                        if (collections_1_1 && !collections_1_1.done && (_b = collections_1.return)) _b.call(collections_1);
                    }
                    finally { if (e_1) throw e_1.error; }
                    return [7 /*endfinally*/];
                case 10: return [2 /*return*/, {
                        has_references: impacts.length > 0,
                        unresolved: unresolved,
                        collections: impacts.sort(function (a, b) { return a.collection.localeCompare(b.collection); })
                    }];
            }
        });
    });
}
function normalizeOptionalString(value) {
    if (typeof value === 'string') {
        var trimmed = value.trim();
        return trimmed.length ? trimmed : undefined;
    }
    if (value === null || value === undefined) {
        return undefined;
    }
    var normalized = String(value).trim();
    return normalized.length ? normalized : undefined;
}
function normalizePositiveNumber(value, fallback) {
    var parsed = Number(value);
    if (!Number.isFinite(parsed) || parsed <= 0) {
        return fallback;
    }
    return parsed;
}
function parseBooleanValue(value, fallback) {
    if (typeof value === 'boolean') {
        return value;
    }
    var normalized = String(value || '').trim().toLowerCase();
    if (!normalized.length) {
        return fallback;
    }
    if (['1', 'true', 'yes', 'on'].includes(normalized)) {
        return true;
    }
    if (['0', 'false', 'no', 'off'].includes(normalized)) {
        return false;
    }
    return fallback;
}
function truncateForRiskReview(value, maxLength) {
    if (maxLength === void 0) { maxLength = MAX_REVIEW_STRING_LENGTH; }
    var normalized = String(value || '');
    if (normalized.length <= maxLength) {
        return normalized;
    }
    var diff = normalized.length - maxLength;
    return "".concat(normalized.slice(0, maxLength), "...[+").concat(diff, " chars]");
}
function sanitizeForRiskReview(value, depth) {
    if (depth === void 0) { depth = 0; }
    if (value === null || value === undefined) {
        return value;
    }
    if (depth > MAX_REVIEW_DEPTH) {
        return '[Truncated depth]';
    }
    if (value instanceof Date) {
        return value.toISOString();
    }
    if (typeof value === 'string') {
        return truncateForRiskReview(value);
    }
    if (typeof value === 'number' || typeof value === 'boolean') {
        return value;
    }
    if (Array.isArray(value)) {
        var sanitized = value
            .slice(0, MAX_REVIEW_ARRAY_ITEMS)
            .map(function (item) { return sanitizeForRiskReview(item, depth + 1); });
        if (value.length > MAX_REVIEW_ARRAY_ITEMS) {
            sanitized.push("[".concat(value.length - MAX_REVIEW_ARRAY_ITEMS, " more item(s)]"));
        }
        return sanitized;
    }
    if (typeof value === 'object') {
        var output_1 = {};
        var keys = Object.keys(value);
        var limitedKeys = keys.slice(0, MAX_REVIEW_OBJECT_KEYS);
        limitedKeys.forEach(function (key) {
            if (SENSITIVE_REVIEW_FIELD_REGEX.test(key)) {
                output_1[key] = '[REDACTED]';
                return;
            }
            output_1[key] = sanitizeForRiskReview(value[key], depth + 1);
        });
        if (keys.length > MAX_REVIEW_OBJECT_KEYS) {
            output_1.__truncated_keys = keys.length - MAX_REVIEW_OBJECT_KEYS;
        }
        return output_1;
    }
    return truncateForRiskReview(String(value));
}
function pickChangedFields(beforeDocument, afterDocument) {
    var e_2, _a;
    if (!beforeDocument || typeof beforeDocument !== 'object' || !afterDocument || typeof afterDocument !== 'object') {
        return [];
    }
    var changed = [];
    var fieldNames = new Set(__spreadArray(__spreadArray([], __read(Object.keys(beforeDocument)), false), __read(Object.keys(afterDocument)), false));
    try {
        for (var fieldNames_1 = __values(fieldNames), fieldNames_1_1 = fieldNames_1.next(); !fieldNames_1_1.done; fieldNames_1_1 = fieldNames_1.next()) {
            var field = fieldNames_1_1.value;
            if (changed.length >= MAX_REVIEW_CHANGED_FIELDS) {
                break;
            }
            var beforeValue = safeStringify(beforeDocument[field]);
            var afterValue = safeStringify(afterDocument[field]);
            if (beforeValue !== afterValue) {
                changed.push(field);
            }
        }
    }
    catch (e_2_1) { e_2 = { error: e_2_1 }; }
    finally {
        try {
            if (fieldNames_1_1 && !fieldNames_1_1.done && (_a = fieldNames_1.return)) _a.call(fieldNames_1);
        }
        finally { if (e_2) throw e_2.error; }
    }
    return changed;
}
function normalizeRiskLevel(value) {
    var normalized = String(value || '').trim().toLowerCase();
    if (normalized === 'low') {
        return 'low';
    }
    if (normalized === 'medium') {
        return 'medium';
    }
    if (normalized === 'high') {
        return 'high';
    }
    if (normalized === 'critical') {
        return 'critical';
    }
    return 'high';
}
function normalizeRiskList(value) {
    if (!Array.isArray(value)) {
        return [];
    }
    return value
        .map(function (item) { return normalizeOptionalString(item); })
        .filter(function (item) { return !!item; })
        .slice(0, MAX_REVIEW_LIST_ITEMS)
        .map(function (item) { return truncateForRiskReview(item, 220); });
}
function buildFallbackRiskReview(operation, summary, model) {
    if (model === void 0) { model = ''; }
    return {
        operation: operation,
        risk_level: 'high',
        should_block: true,
        summary: truncateForRiskReview(summary, 220),
        reasons: [
            'AI review could not produce a reliable result.',
            'Proceed only with explicit approval and manual dependency checks.'
        ],
        suggested_checks: [
            'Confirm related records and id_* references before writing.',
            'Validate that schema-required fields and date values remain valid.'
        ],
        confidence: 0,
        model: model || '',
        request_id: '',
        review_status: 'fallback',
        reviewed_at: new Date().toISOString()
    };
}
function buildDisabledRiskReview(operation) {
    return {
        operation: operation,
        risk_level: 'low',
        should_block: false,
        summary: 'AI risk review is disabled by configuration.',
        reasons: [],
        suggested_checks: [],
        confidence: 1,
        model: '',
        request_id: '',
        review_status: 'disabled',
        reviewed_at: new Date().toISOString()
    };
}
function parseRiskReviewPayload(content) {
    var normalized = String(content || '').trim();
    if (!normalized.length) {
        return {};
    }
    try {
        return JSON.parse(normalized);
    }
    catch (_a) {
        // Fall through and try to parse a JSON object embedded in plain text.
    }
    var start = normalized.indexOf('{');
    var end = normalized.lastIndexOf('}');
    if (start !== -1 && end > start) {
        var candidate = normalized.slice(start, end + 1);
        try {
            return JSON.parse(candidate);
        }
        catch (_b) {
            return {};
        }
    }
    return {};
}
function normalizeRiskReview(operation, payload, model, requestId) {
    var riskLevel = normalizeRiskLevel(payload === null || payload === void 0 ? void 0 : payload.risk_level);
    var normalizedConfidence = Number(payload === null || payload === void 0 ? void 0 : payload.confidence);
    var confidence = Number.isFinite(normalizedConfidence)
        ? Math.max(0, Math.min(1, normalizedConfidence))
        : 0.6;
    var summary = normalizeOptionalString(payload === null || payload === void 0 ? void 0 : payload.summary)
        || "AI review marked this operation as ".concat(riskLevel, " risk.");
    var reasons = normalizeRiskList(payload === null || payload === void 0 ? void 0 : payload.reasons);
    var suggestedChecks = normalizeRiskList(payload === null || payload === void 0 ? void 0 : payload.suggested_checks);
    var shouldBlock = (payload === null || payload === void 0 ? void 0 : payload.should_block) === true || riskLevel === 'critical';
    return {
        operation: operation,
        risk_level: riskLevel,
        should_block: !!shouldBlock,
        summary: truncateForRiskReview(summary, 220),
        reasons: reasons,
        suggested_checks: suggestedChecks,
        confidence: confidence,
        model: model || '',
        request_id: requestId || '',
        review_status: 'ok',
        reviewed_at: new Date().toISOString()
    };
}
function resolveRiskReviewSettings() {
    var _a, _b, _c, _d, _e;
    var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
    var enabled = parseBooleanValue((_a = serverConfig['MONGO_EXPLORER_ENABLE_AI_RISK_REVIEW']) !== null && _a !== void 0 ? _a : process.env.MONGO_EXPLORER_ENABLE_AI_RISK_REVIEW, true);
    var model = normalizeOptionalString(serverConfig['MONGO_EXPLORER_RISK_REVIEW_MODEL']
        || process.env.MONGO_EXPLORER_RISK_REVIEW_MODEL
        || serverConfig['AI_ASSISTANT_CODEX_MODEL']
        || process.env.AI_ASSISTANT_CODEX_MODEL
        || serverConfig['AI_DASHBOARD_CODEX_MODEL']
        || process.env.AI_DASHBOARD_CODEX_MODEL);
    var fallbackModel = normalizeOptionalString(serverConfig['MONGO_EXPLORER_RISK_REVIEW_FALLBACK_MODEL']
        || process.env.MONGO_EXPLORER_RISK_REVIEW_FALLBACK_MODEL
        || serverConfig['AI_ASSISTANT_CODEX_FALLBACK_MODEL']
        || process.env.AI_ASSISTANT_CODEX_FALLBACK_MODEL
        || serverConfig['AI_DASHBOARD_CODEX_FALLBACK_MODEL']
        || process.env.AI_DASHBOARD_CODEX_FALLBACK_MODEL);
    var timeoutMs = normalizePositiveNumber((_b = serverConfig['MONGO_EXPLORER_RISK_REVIEW_TIMEOUT_MS']) !== null && _b !== void 0 ? _b : process.env.MONGO_EXPLORER_RISK_REVIEW_TIMEOUT_MS, DEFAULT_RISK_REVIEW_TIMEOUT_MS);
    var maxTokens = normalizePositiveNumber((_c = serverConfig['MONGO_EXPLORER_RISK_REVIEW_MAX_TOKENS']) !== null && _c !== void 0 ? _c : process.env.MONGO_EXPLORER_RISK_REVIEW_MAX_TOKENS, DEFAULT_RISK_REVIEW_MAX_TOKENS);
    var maxRetries = normalizePositiveNumber((_d = serverConfig['OPENAI_MAX_RETRIES']) !== null && _d !== void 0 ? _d : process.env.OPENAI_MAX_RETRIES, 1);
    var retryDelayMs = normalizePositiveNumber((_e = serverConfig['OPENAI_RETRY_DELAY_MS']) !== null && _e !== void 0 ? _e : process.env.OPENAI_RETRY_DELAY_MS, 750);
    var apiKey = normalizeOptionalString(serverConfig['OPENAI_API_KEY'] || process.env.OPENAI_API_KEY) || '';
    var baseUrl = normalizeOptionalString(serverConfig['OPENAI_BASE_URL'] || process.env.OPENAI_BASE_URL);
    return {
        enabled: enabled,
        apiKey: apiKey,
        baseUrl: baseUrl,
        model: model,
        fallbackModel: fallbackModel,
        timeoutMs: timeoutMs,
        maxTokens: maxTokens,
        maxRetries: maxRetries,
        retryDelayMs: retryDelayMs
    };
}
function buildCodexPrompt(systemPrompt, userPrompt) {
    return "System:\n".concat(systemPrompt, "\n\nUser:\n").concat(userPrompt).trim();
}
function buildCodexClient(settings) {
    var fallbackModels = [];
    var fallback = normalizeOptionalString(settings.fallbackModel);
    if (fallback && fallback !== settings.model) {
        fallbackModels.push(fallback);
    }
    return new codex_client_1.CodexClient(__assign(__assign(__assign({ apiKey: settings.apiKey, baseUrl: settings.baseUrl }, (settings.model ? { model: settings.model } : {})), (fallbackModels.length ? { fallbackModel: fallbackModels[0], fallbackModels: fallbackModels } : {})), { maxRetries: (0, common_1.round)(settings.maxRetries || 0), retryDelayMs: (0, common_1.round)(settings.retryDelayMs || 0) }));
}
function estimateCodexUsage(messages, responseText, model) {
    var inputTokens = (0, tokenizer_1.countChatTokens)(messages, model);
    var outputTokens = (0, tokenizer_1.countTokens)(responseText || '', model);
    return {
        inputTokens: inputTokens,
        outputTokens: outputTokens,
        totalTokens: inputTokens + outputTokens
    };
}
function buildRiskReviewPrompt(input) {
    var payload = {
        database: input.database,
        collection: input.collection,
        operation: input.operation,
        mode: input.mode,
        changed_fields: pickChangedFields(input.before_document, input.after_document),
        delete_impact: sanitizeForRiskReview(input.delete_impact || null),
        before_document: sanitizeForRiskReview(input.before_document || null),
        after_document: sanitizeForRiskReview(input.after_document || null)
    };
    return [
        'Review this MongoDB write operation for runtime and data integrity risk.',
        'Focus on foreign-key style id_* dependencies, schema compatibility, destructive impact, and financial/operational record safety.',
        'Return JSON only.',
        JSON.stringify(payload, null, 2)
    ].join('\n');
}
function reviewOperationRisk(input) {
    return __awaiter(this, void 0, void 0, function () {
        var settings, client, systemPrompt, userPrompt, prompt, responseText, payload, err_1, detail;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    settings = resolveRiskReviewSettings();
                    if (!settings.enabled) {
                        return [2 /*return*/, buildDisabledRiskReview(input.operation)];
                    }
                    if (!settings.apiKey) {
                        return [2 /*return*/, buildFallbackRiskReview(input.operation, 'AI risk review unavailable: AI API key is missing.')];
                    }
                    client = buildCodexClient(settings);
                    systemPrompt = [
                        'You are a MongoDB operation safety reviewer for a production SaaS application.',
                        'Respond with a single JSON object only.',
                        'Required keys:',
                        'risk_level ("low" | "medium" | "high" | "critical"),',
                        'should_block (boolean),',
                        'summary (string),',
                        'reasons (string[]),',
                        'suggested_checks (string[]),',
                        'confidence (number between 0 and 1).',
                        'Keep summary concise (<= 220 chars).'
                    ].join(' ');
                    userPrompt = buildRiskReviewPrompt(input);
                    prompt = buildCodexPrompt(systemPrompt, userPrompt);
                    _a.label = 1;
                case 1:
                    _a.trys.push([1, 3, , 4]);
                    return [4 /*yield*/, client.run(prompt, {
                            timeoutMs: (0, common_1.round)(settings.timeoutMs),
                            threadOptions: {
                                model: settings.model,
                                sandboxMode: 'read-only',
                                skipGitRepoCheck: true,
                                networkAccessEnabled: false,
                                webSearchMode: 'disabled',
                                webSearchEnabled: false,
                                approvalPolicy: 'never'
                            }
                        })];
                case 2:
                    responseText = _a.sent();
                    payload = parseRiskReviewPayload(responseText);
                    return [2 /*return*/, normalizeRiskReview(input.operation, payload, settings.model, '')];
                case 3:
                    err_1 = _a.sent();
                    detail = (err_1 === null || err_1 === void 0 ? void 0 : err_1.message) ? String(err_1.message) : 'Unknown AI review error';
                    return [2 /*return*/, buildFallbackRiskReview(input.operation, "AI risk review failed: ".concat(truncateForRiskReview(detail, 160)), settings.model)];
                case 4: return [2 /*return*/];
            }
        });
    });
}
function normalizeAiAction(value) {
    var normalized = normalizeOptionalString(value) || '';
    return normalized.toLowerCase() === 'aggregate' ? 'aggregate' : 'find';
}
function sanitizeAiCollections(raw) {
    var values = [];
    var seen = new Set();
    (raw || []).forEach(function (entry) {
        var normalized = normalizeOptionalString((entry === null || entry === void 0 ? void 0 : entry.collection) || (entry === null || entry === void 0 ? void 0 : entry.name) || entry);
        if (!normalized) {
            return;
        }
        var key = normalized.toLowerCase();
        if (seen.has(key)) {
            return;
        }
        seen.add(key);
        values.push(normalized);
    });
    return values.slice(0, MAX_AI_SUGGEST_COLLECTIONS);
}
function sanitizeAiFields(raw, fallbackCollection) {
    var fields = [];
    var seen = new Set();
    (raw || []).forEach(function (entry) {
        var path = normalizeOptionalString((entry === null || entry === void 0 ? void 0 : entry.path) || (entry === null || entry === void 0 ? void 0 : entry.field_path) || (entry === null || entry === void 0 ? void 0 : entry.fieldPath));
        if (!path) {
            return;
        }
        var collection = normalizeOptionalString((entry === null || entry === void 0 ? void 0 : entry.collection) || (entry === null || entry === void 0 ? void 0 : entry.collection_name) || (entry === null || entry === void 0 ? void 0 : entry.collectionName)) || fallbackCollection || '';
        var key = "".concat(collection.toLowerCase(), "::").concat(path.toLowerCase());
        if (seen.has(key)) {
            return;
        }
        seen.add(key);
        fields.push({
            path: path,
            label: normalizeOptionalString((entry === null || entry === void 0 ? void 0 : entry.label) || (entry === null || entry === void 0 ? void 0 : entry.field_path_name) || (entry === null || entry === void 0 ? void 0 : entry.fieldPathName)) || path,
            type: normalizeOptionalString((entry === null || entry === void 0 ? void 0 : entry.type) || (entry === null || entry === void 0 ? void 0 : entry.field_type) || (entry === null || entry === void 0 ? void 0 : entry.fieldType)) || 'Any',
            collection: collection
        });
    });
    return fields.slice(0, MAX_AI_SUGGEST_FIELDS);
}
function deriveAiFieldsFromCollection(database, collection) {
    if (!collection) {
        return [];
    }
    var managerCollection = resolveCollectionHandle(database, collection).managerCollection;
    if (!managerCollection) {
        return [];
    }
    var schema = getSchemaDefinition(managerCollection);
    return Object.keys(schema)
        .sort(function (a, b) { return a.localeCompare(b); })
        .slice(0, MAX_AI_SUGGEST_FIELDS)
        .map(function (path) {
        var definition = schema[path];
        return {
            path: path,
            label: toLabel(path),
            type: getSchemaTypeName(definition),
            collection: collection
        };
    });
}
function resolveCollectionFromList(value, collections, fallback) {
    if (fallback === void 0) { fallback = ''; }
    var normalized = normalizeOptionalString(value);
    if (normalized) {
        var matched = (collections || []).find(function (entry) { return entry.toLowerCase() === normalized.toLowerCase(); });
        if (matched) {
            return matched;
        }
    }
    if (fallback) {
        return fallback;
    }
    return (collections === null || collections === void 0 ? void 0 : collections[0]) || '';
}
function normalizeAiResultLimit(value, fallback) {
    if (fallback === void 0) { fallback = 100; }
    var fallbackValue = Math.min(Math.max(fallback, 1), MAX_AI_SUGGEST_RESULTS);
    var parsed = Number(value);
    if (!Number.isFinite(parsed) || parsed <= 0) {
        return fallbackValue;
    }
    return Math.min(Math.max((0, common_1.round)(parsed), 1), MAX_AI_SUGGEST_RESULTS);
}
function normalizeAiSkip(value) {
    var parsed = Number(value);
    if (!Number.isFinite(parsed) || parsed < 0) {
        return 0;
    }
    return Math.max(0, (0, common_1.round)(parsed));
}
function normalizeAiObject(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        return {};
    }
    return value;
}
function hasObjectKeys(value) {
    return !!value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length > 0;
}
function parseAiPlanPayload(content) {
    var normalized = String(content || '').trim();
    if (!normalized.length) {
        return {};
    }
    try {
        return JSON.parse(normalized);
    }
    catch (_a) {
        // Fall through and parse embedded JSON object.
    }
    var start = normalized.indexOf('{');
    var end = normalized.lastIndexOf('}');
    if (start !== -1 && end > start) {
        try {
            return JSON.parse(normalized.slice(start, end + 1));
        }
        catch (_b) {
            return {};
        }
    }
    return {};
}
function resolveMongoExplorerAiSettings() {
    var _a, _b, _c, _d;
    var serverConfig = resolveio_server_app_1.ResolveIOServer.getServerConfig() || {};
    var apiKey = normalizeOptionalString(serverConfig['OPENAI_API_KEY'] || process.env.OPENAI_API_KEY) || '';
    var baseUrl = normalizeOptionalString(serverConfig['OPENAI_BASE_URL'] || process.env.OPENAI_BASE_URL);
    var model = normalizeOptionalString(serverConfig['MONGO_EXPLORER_AI_MODEL']
        || process.env.MONGO_EXPLORER_AI_MODEL
        || serverConfig['AI_ASSISTANT_CODEX_MODEL']
        || process.env.AI_ASSISTANT_CODEX_MODEL);
    var fallbackModel = normalizeOptionalString(serverConfig['MONGO_EXPLORER_AI_FALLBACK_MODEL']
        || process.env.MONGO_EXPLORER_AI_FALLBACK_MODEL
        || serverConfig['AI_ASSISTANT_CODEX_FALLBACK_MODEL']
        || process.env.AI_ASSISTANT_CODEX_FALLBACK_MODEL);
    var timeoutMs = normalizePositiveNumber((_a = serverConfig['MONGO_EXPLORER_AI_TIMEOUT_MS']) !== null && _a !== void 0 ? _a : process.env.MONGO_EXPLORER_AI_TIMEOUT_MS, DEFAULT_AI_SUGGEST_TIMEOUT_MS);
    var maxTokens = normalizePositiveNumber((_b = serverConfig['MONGO_EXPLORER_AI_MAX_TOKENS']) !== null && _b !== void 0 ? _b : process.env.MONGO_EXPLORER_AI_MAX_TOKENS, DEFAULT_AI_SUGGEST_MAX_TOKENS);
    var maxRetries = normalizePositiveNumber((_c = serverConfig['OPENAI_MAX_RETRIES']) !== null && _c !== void 0 ? _c : process.env.OPENAI_MAX_RETRIES, 1);
    var retryDelayMs = normalizePositiveNumber((_d = serverConfig['OPENAI_RETRY_DELAY_MS']) !== null && _d !== void 0 ? _d : process.env.OPENAI_RETRY_DELAY_MS, 750);
    return {
        apiKey: apiKey,
        baseUrl: baseUrl,
        model: model,
        fallbackModel: fallbackModel,
        timeoutMs: timeoutMs,
        maxTokens: maxTokens,
        maxRetries: maxRetries,
        retryDelayMs: retryDelayMs
    };
}
function buildMongoExplorerAiSystemPrompt() {
    return [
        'You are a MongoDB query planner for a read-only Mongo Explorer UI.',
        'Return a single JSON object and nothing else.',
        'Supported schema:',
        '{',
        '  "action": "find|aggregate",',
        '  "collection": "string",',
        '  "query": { ... },',
        '  "pipeline": [{ ... }],',
        '  "options": {',
        '    "projection": { "field": 1 },',
        '    "sort": { "field": -1 },',
        '    "limit": 100,',
        '    "skip": 0,',
        '    "includeTotal": true,',
        '    "allowDiskUse": true',
        '  },',
        '  "notes": "short summary"',
        '}',
        'Rules:',
        '- Use only provided collection names and field paths.',
        '- Read-only only. Never output write operations, update/delete commands, or aggregation stages $out/$merge.',
        '- Prefer action=find for direct lookups and simple filters.',
        '- Use action=aggregate for grouped totals, rankings, or trend buckets.',
        '- Keep options.limit between 1 and 500.',
        '- If request is ambiguous, use selected_collection and a conservative limit.'
    ].join('\n');
}
function buildMongoExplorerAiUserPrompt(input) {
    var payload = {
        request: input.prompt,
        selected_collection: input.selectedCollection || '',
        max_results: input.maxResults,
        available_collections: input.availableCollections || [],
        available_fields: input.availableFields || []
    };
    return JSON.stringify(payload);
}
function sanitizeAggregatePipeline(rawPipeline, limit) {
    var source = Array.isArray(rawPipeline) ? rawPipeline : [];
    var sanitized = [];
    var removedRestricted = false;
    source.forEach(function (stage) {
        if (!stage || typeof stage !== 'object' || Array.isArray(stage)) {
            return;
        }
        var keys = Object.keys(stage);
        if (!keys.length) {
            return;
        }
        var stageName = keys[0];
        if (RESTRICTED_AGGREGATE_STAGES.has(String(stageName).toLowerCase())) {
            removedRestricted = true;
            return;
        }
        if (stageName === '$limit') {
            sanitized.push({ $limit: normalizeAiResultLimit(stage.$limit, limit) });
            return;
        }
        sanitized.push(stage);
    });
    if (!sanitized.some(function (stage) { return Object.keys(stage || {})[0] === '$limit'; })) {
        sanitized.push({ $limit: limit });
    }
    return {
        pipeline: sanitized,
        removedRestricted: removedRestricted
    };
}
function sanitizeFindOptions(raw, fallbackLimit) {
    var options = normalizeAiObject(raw);
    return {
        projection: hasObjectKeys(options.projection) ? options.projection : undefined,
        sort: hasObjectKeys(options.sort) ? options.sort : undefined,
        limit: normalizeAiResultLimit(options.limit, fallbackLimit),
        skip: normalizeAiSkip(options.skip),
        includeTotal: options.includeTotal === true
    };
}
function buildMongoExplorerAiNotes(rawNotes, action, collection, rowCount, total, removedRestricted) {
    var notes = [];
    var base = normalizeOptionalString(rawNotes);
    if (base) {
        notes.push(base);
    }
    var actionLabel = action === 'aggregate' ? 'aggregate pipeline' : 'query';
    if (typeof total === 'number' && total >= 0) {
        notes.push("Applied ".concat(actionLabel, " on ").concat(collection, ". Loaded ").concat(rowCount, " of ").concat(total, " rows."));
    }
    else {
        notes.push("Applied ".concat(actionLabel, " on ").concat(collection, ". Loaded ").concat(rowCount, " rows."));
    }
    if (removedRestricted) {
        notes.push('Removed restricted write stages from the generated pipeline.');
    }
    return notes.join(' ').trim();
}
function resolveUsageClientId(idClientInput, idUser) {
    return __awaiter(this, void 0, void 0, function () {
        var normalized, user, _a;
        var _b, _c;
        return __generator(this, function (_d) {
            switch (_d.label) {
                case 0:
                    normalized = normalizeOptionalString(idClientInput);
                    if (normalized) {
                        return [2 /*return*/, normalized];
                    }
                    if (!idUser) {
                        return [2 /*return*/, ''];
                    }
                    _d.label = 1;
                case 1:
                    _d.trys.push([1, 3, , 4]);
                    return [4 /*yield*/, user_collection_1.Users.findById(idUser)];
                case 2:
                    user = _d.sent();
                    return [2 /*return*/, normalizeOptionalString(((_b = user === null || user === void 0 ? void 0 : user.other) === null || _b === void 0 ? void 0 : _b.id_client) || ((_c = user === null || user === void 0 ? void 0 : user.other) === null || _c === void 0 ? void 0 : _c.idClient)) || ''];
                case 3:
                    _a = _d.sent();
                    return [2 /*return*/, ''];
                case 4: return [2 /*return*/];
            }
        });
    });
}
function executeMongoExplorerAi(payload, context) {
    return __awaiter(this, void 0, void 0, function () {
        var input, prompt, database, db, availableCollections, listed, selectedCollection, availableFields, settings, maxResults, client, messages, responseText, parsed, action, collection, plan, removedRestrictedStages, optionsRaw, aggregateLimit, pipelineResult, aggregateOptions, aggregateRows, rows, query, options, findOptions, rows, total, _a, usage, idClient;
        var _b;
        return __generator(this, function (_c) {
            switch (_c.label) {
                case 0:
                    input = payload || {};
                    prompt = normalizeOptionalString(input.prompt);
                    if (!prompt) {
                        throw new Error('Prompt is required.');
                    }
                    database = resolveDatabaseName(input.database);
                    db = resolveDatabase(database);
                    availableCollections = sanitizeAiCollections(input.available_collections || []);
                    if (!!availableCollections.length) return [3 /*break*/, 2];
                    return [4 /*yield*/, db.listCollections().toArray()];
                case 1:
                    listed = _c.sent();
                    availableCollections = listed
                        .map(function (collection) { return normalizeOptionalString(collection === null || collection === void 0 ? void 0 : collection.name); })
                        .filter(function (collection) { return !!collection; })
                        .slice(0, MAX_AI_SUGGEST_COLLECTIONS);
                    _c.label = 2;
                case 2:
                    if (!availableCollections.length) {
                        throw new Error('Mongo Explorer AI assistant: no collections available.');
                    }
                    selectedCollection = resolveCollectionFromList(input.selected_collection, availableCollections, availableCollections[0]);
                    availableFields = sanitizeAiFields(input.available_fields || [], selectedCollection);
                    if (!availableFields.length && selectedCollection) {
                        availableFields = deriveAiFieldsFromCollection(database, selectedCollection);
                    }
                    settings = resolveMongoExplorerAiSettings();
                    if (!settings.apiKey) {
                        throw new Error('AI API key missing. Add an AI API key to server config.');
                    }
                    maxResults = normalizeAiResultLimit(input.max_results, 100);
                    client = buildCodexClient(settings);
                    messages = [
                        { role: 'system', content: buildMongoExplorerAiSystemPrompt() },
                        {
                            role: 'user',
                            content: buildMongoExplorerAiUserPrompt({
                                prompt: prompt,
                                selectedCollection: selectedCollection,
                                availableCollections: availableCollections,
                                availableFields: availableFields,
                                maxResults: maxResults
                            })
                        }
                    ];
                    return [4 /*yield*/, client.run(buildCodexPrompt(messages[0].content, messages[1].content), {
                            timeoutMs: (0, common_1.round)(settings.timeoutMs),
                            threadOptions: {
                                model: settings.model,
                                sandboxMode: 'read-only',
                                skipGitRepoCheck: true,
                                networkAccessEnabled: false,
                                webSearchMode: 'disabled',
                                webSearchEnabled: false,
                                approvalPolicy: 'never'
                            }
                        })];
                case 3:
                    responseText = _c.sent();
                    parsed = parseAiPlanPayload(responseText);
                    action = normalizeAiAction((parsed === null || parsed === void 0 ? void 0 : parsed.action) || (Array.isArray(parsed === null || parsed === void 0 ? void 0 : parsed.pipeline) ? 'aggregate' : 'find'));
                    collection = resolveCollectionFromList((parsed === null || parsed === void 0 ? void 0 : parsed.collection) || (parsed === null || parsed === void 0 ? void 0 : parsed.collection_name), availableCollections, selectedCollection || availableCollections[0]);
                    if (!collection) {
                        throw new Error('Mongo Explorer AI assistant could not resolve a collection.');
                    }
                    plan = {
                        action: action,
                        collection: collection,
                        options: {}
                    };
                    removedRestrictedStages = false;
                    if (!(action === 'aggregate')) return [3 /*break*/, 5];
                    optionsRaw = normalizeAiObject(parsed === null || parsed === void 0 ? void 0 : parsed.options);
                    aggregateLimit = normalizeAiResultLimit((_b = optionsRaw.limit) !== null && _b !== void 0 ? _b : parsed === null || parsed === void 0 ? void 0 : parsed.limit, maxResults);
                    pipelineResult = sanitizeAggregatePipeline(parsed === null || parsed === void 0 ? void 0 : parsed.pipeline, aggregateLimit);
                    removedRestrictedStages = pipelineResult.removedRestricted;
                    aggregateOptions = {
                        allowDiskUse: optionsRaw.allowDiskUse === true
                    };
                    return [4 /*yield*/, db.collection(collection).aggregate(pipelineResult.pipeline, aggregateOptions).toArray()];
                case 4:
                    aggregateRows = _c.sent();
                    rows = (aggregateRows || []).slice(0, aggregateLimit);
                    plan = {
                        action: action,
                        collection: collection,
                        pipeline: pipelineResult.pipeline,
                        options: {
                            allowDiskUse: aggregateOptions.allowDiskUse,
                            limit: aggregateLimit
                        },
                        documents: rows,
                        total: null
                    };
                    return [3 /*break*/, 10];
                case 5:
                    query = normalizeAiObject(parsed === null || parsed === void 0 ? void 0 : parsed.query);
                    options = sanitizeFindOptions(parsed === null || parsed === void 0 ? void 0 : parsed.options, maxResults);
                    findOptions = {
                        limit: options.limit,
                        skip: options.skip
                    };
                    if (options.projection) {
                        findOptions.projection = options.projection;
                    }
                    if (options.sort) {
                        findOptions.sort = options.sort;
                    }
                    return [4 /*yield*/, db.collection(collection).find(query, findOptions).toArray()];
                case 6:
                    rows = _c.sent();
                    if (!options.includeTotal) return [3 /*break*/, 8];
                    return [4 /*yield*/, db.collection(collection).countDocuments(query)];
                case 7:
                    _a = _c.sent();
                    return [3 /*break*/, 9];
                case 8:
                    _a = null;
                    _c.label = 9;
                case 9:
                    total = _a;
                    plan = {
                        action: action,
                        collection: collection,
                        query: query,
                        options: __assign(__assign(__assign({}, (options.projection ? { projection: options.projection } : {})), (options.sort ? { sort: options.sort } : {})), { limit: options.limit, skip: options.skip, includeTotal: options.includeTotal }),
                        documents: rows,
                        total: total
                    };
                    _c.label = 10;
                case 10:
                    plan.notes = buildMongoExplorerAiNotes(parsed === null || parsed === void 0 ? void 0 : parsed.notes, plan.action, plan.collection, Array.isArray(plan.documents) ? plan.documents.length : 0, typeof plan.total === 'number' ? plan.total : null, removedRestrictedStages);
                    usage = estimateCodexUsage(messages, responseText, settings.model);
                    return [4 /*yield*/, resolveUsageClientId(input.id_client, context === null || context === void 0 ? void 0 : context.id_user)];
                case 11:
                    idClient = _c.sent();
                    if (!usage.totalTokens) return [3 /*break*/, 13];
                    return [4 /*yield*/, (0, openai_usage_ledger_manager_1.recordOpenAIUsage)({
                            id_client: idClient || '',
                            model: settings.model || 'unknown',
                            input_tokens: usage.inputTokens || 0,
                            output_tokens: usage.outputTokens || 0,
                            total_tokens: usage.totalTokens || 0,
                            category: 'mongo-explorer-ai',
                            id_request: ''
                        })];
                case 12:
                    _c.sent();
                    _c.label = 13;
                case 13: return [2 /*return*/, {
                        notes: plan.notes,
                        plan: plan,
                        model: settings.model,
                        usage: {
                            input_tokens: usage.inputTokens || 0,
                            output_tokens: usage.outputTokens || 0,
                            total_tokens: usage.totalTokens || 0
                        }
                    }];
            }
        });
    });
}
function loadMongoExplorerMethods(methodManager) {
    methodManager.methods({
        mongoExplorerAiSuggest: {
            check: new simpl_schema_1.default({
                payload: {
                    type: Object,
                    blackbox: true
                }
            }),
            function: function (payload) {
                return __awaiter(this, void 0, void 0, function () {
                    return __generator(this, function (_a) {
                        return [2 /*return*/, executeMongoExplorerAi(payload || {}, this)];
                    });
                });
            }
        },
        mongoExplorerListCollections: {
            check: new simpl_schema_1.default({
                database: {
                    type: String,
                    optional: true
                }
            }),
            function: function (database) {
                return __awaiter(this, void 0, void 0, function () {
                    var db, collections;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0:
                                db = resolveDatabase(database);
                                return [4 /*yield*/, db.listCollections().toArray()];
                            case 1:
                                collections = _a.sent();
                                return [2 /*return*/, collections
                                        .map(function (col) { return ({
                                        name: col.name,
                                        type: col.type || 'collection'
                                    }); })
                                        .sort(function (a, b) { return a.name.localeCompare(b.name); })];
                        }
                    });
                });
            }
        },
        mongoExplorerFind: {
            bypassSession: true,
            check: new simpl_schema_1.default({
                database: {
                    type: String,
                    optional: true
                },
                collection: {
                    type: String
                },
                query: {
                    type: Object,
                    blackbox: true,
                    optional: true
                },
                options: {
                    type: Object,
                    blackbox: true,
                    optional: true
                }
            }),
            function: function (database_1, collection_1) {
                return __awaiter(this, arguments, void 0, function (database, collection, query, options) {
                    var db, normalized, documents, total;
                    if (query === void 0) { query = {}; }
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0:
                                db = resolveDatabase(database);
                                normalized = normalizeFindOptions(options);
                                return [4 /*yield*/, db.collection(collection).find(query || {}, normalized.findOptions).toArray()];
                            case 1:
                                documents = _a.sent();
                                total = null;
                                if (!normalized.includeTotal) return [3 /*break*/, 3];
                                return [4 /*yield*/, db.collection(collection).countDocuments(query || {})];
                            case 2:
                                total = _a.sent();
                                _a.label = 3;
                            case 3: return [2 /*return*/, {
                                    documents: documents,
                                    total: total
                                }];
                        }
                    });
                });
            }
        },
        mongoExplorerGetCollectionSchema: {
            check: new simpl_schema_1.default({
                database: {
                    type: String,
                    optional: true
                },
                collection: {
                    type: String
                }
            }),
            function: function (database, collection) {
                var dbName = resolveDatabaseName(database);
                var managerCollection = resolveCollectionHandle(dbName, collection).managerCollection;
                if (!managerCollection) {
                    return Promise.resolve({
                        collection: collection,
                        has_schema: false,
                        fields: []
                    });
                }
                var schema = getSchemaDefinition(managerCollection);
                var fields = Object.keys(schema)
                    .sort(function (a, b) { return a.localeCompare(b); })
                    .map(function (path) {
                    var definition = schema[path];
                    return {
                        path: path,
                        label: toLabel(path),
                        type: getSchemaTypeName(definition),
                        optional: (definition === null || definition === void 0 ? void 0 : definition.optional) === true,
                        protected: isProtectedFieldPath(path),
                        depth: path.split('.').filter(function (segment) { return segment !== '$'; }).length - 1
                    };
                });
                return Promise.resolve({
                    collection: collection,
                    has_schema: true,
                    fields: fields
                });
            }
        },
        mongoExplorerAggregate: {
            bypassSession: true,
            check: new simpl_schema_1.default({
                database: {
                    type: String,
                    optional: true
                },
                collection: {
                    type: String
                },
                pipeline: {
                    type: Array
                },
                'pipeline.$': {
                    type: Object,
                    blackbox: true
                },
                options: {
                    type: Object,
                    optional: true,
                    blackbox: true
                }
            }),
            function: function (database_1, collection_1, pipeline_1) {
                return __awaiter(this, arguments, void 0, function (database, collection, pipeline, options) {
                    var db;
                    if (options === void 0) { options = {}; }
                    return __generator(this, function (_a) {
                        db = resolveDatabase(database);
                        return [2 /*return*/, db.collection(collection).aggregate(pipeline || [], options || undefined).toArray()];
                    });
                });
            }
        },
        mongoExplorerCommand: {
            check: new simpl_schema_1.default({
                database: {
                    type: String,
                    optional: true
                },
                command: {
                    type: Object,
                    blackbox: true
                },
                permissionView: {
                    type: String,
                    optional: true
                },
                mode: {
                    type: String,
                    optional: true
                }
            }),
            function: function (database, command, permissionView, mode) {
                return __awaiter(this, void 0, void 0, function () {
                    var db;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0: return [4 /*yield*/, ensureWriteAccess(this, permissionView, mode)];
                            case 1:
                                _a.sent();
                                db = resolveDatabase(database);
                                return [2 /*return*/, db.command(command)];
                        }
                    });
                });
            }
        },
        mongoExplorerInsertDocument: {
            check: new simpl_schema_1.default({
                database: {
                    type: String,
                    optional: true
                },
                collection: {
                    type: String
                },
                document: {
                    type: Object,
                    blackbox: true
                },
                permissionView: {
                    type: String,
                    optional: true
                },
                mode: {
                    type: String,
                    optional: true
                }
            }),
            function: function (database, collection, document, permissionView, mode) {
                return __awaiter(this, void 0, void 0, function () {
                    var dbName, _a, managerCollection, dbCollection;
                    return __generator(this, function (_b) {
                        switch (_b.label) {
                            case 0: return [4 /*yield*/, ensureWriteAccess(this, permissionView, mode)];
                            case 1:
                                _b.sent();
                                dbName = resolveDatabaseName(database);
                                _a = resolveCollectionHandle(dbName, collection), managerCollection = _a.managerCollection, dbCollection = _a.dbCollection;
                                if (!document || typeof document !== 'object') {
                                    throw new Error('Mongo Explorer: Document is required');
                                }
                                if (!managerCollection && !allowUnschemaizedWrites()) {
                                    throw new Error('Mongo Explorer: Writes require schema-backed collections. Set MONGO_EXPLORER_ALLOW_UNSCHEMATIZED_WRITE=true to bypass.');
                                }
                                if (!document._id) {
                                    document._id = (0, common_1.objectIdHexString)();
                                }
                                if (document.__v === undefined) {
                                    document.__v = 0;
                                }
                                if (!managerCollection) return [3 /*break*/, 3];
                                coerceDateFields(managerCollection, document);
                                return [4 /*yield*/, managerCollection.insertOne(document)];
                            case 2:
                                _b.sent();
                                return [3 /*break*/, 5];
                            case 3: return [4 /*yield*/, dbCollection.insertOne(document)];
                            case 4:
                                _b.sent();
                                _b.label = 5;
                            case 5: return [2 /*return*/, document._id];
                        }
                    });
                });
            }
        },
        mongoExplorerReplaceDocument: {
            check: new simpl_schema_1.default({
                database: {
                    type: String,
                    optional: true
                },
                collection: {
                    type: String
                },
                document: {
                    type: Object,
                    blackbox: true
                },
                permissionView: {
                    type: String,
                    optional: true
                },
                mode: {
                    type: String,
                    optional: true
                }
            }),
            function: function (database, collection, document, permissionView, mode) {
                return __awaiter(this, void 0, void 0, function () {
                    var dbName, _a, managerCollection, dbCollection, currentDoc, _b;
                    return __generator(this, function (_c) {
                        switch (_c.label) {
                            case 0: return [4 /*yield*/, ensureWriteAccess(this, permissionView, mode)];
                            case 1:
                                _c.sent();
                                dbName = resolveDatabaseName(database);
                                _a = resolveCollectionHandle(dbName, collection), managerCollection = _a.managerCollection, dbCollection = _a.dbCollection;
                                if (!document || typeof document !== 'object' || !document._id) {
                                    throw new Error('Mongo Explorer: Document with _id is required');
                                }
                                if (!managerCollection) return [3 /*break*/, 3];
                                return [4 /*yield*/, managerCollection.findOne({ _id: document._id })];
                            case 2:
                                _b = _c.sent();
                                return [3 /*break*/, 5];
                            case 3: return [4 /*yield*/, dbCollection.findOne({ _id: document._id })];
                            case 4:
                                _b = _c.sent();
                                _c.label = 5;
                            case 5:
                                currentDoc = _b;
                                if (!currentDoc) {
                                    throw new Error('Mongo Explorer: Document not found');
                                }
                                ensureProtectedFieldsUnchanged(currentDoc, document);
                                if (!managerCollection && !allowUnschemaizedWrites()) {
                                    throw new Error('Mongo Explorer: Writes require schema-backed collections. Set MONGO_EXPLORER_ALLOW_UNSCHEMATIZED_WRITE=true to bypass.');
                                }
                                if (!managerCollection) return [3 /*break*/, 7];
                                coerceDateFields(managerCollection, document);
                                return [4 /*yield*/, managerCollection.replaceOne({ _id: document._id }, document)];
                            case 6:
                                _c.sent();
                                return [3 /*break*/, 9];
                            case 7: return [4 /*yield*/, dbCollection.replaceOne({ _id: document._id }, document)];
                            case 8:
                                _c.sent();
                                _c.label = 9;
                            case 9: return [2 /*return*/, true];
                        }
                    });
                });
            }
        },
        mongoExplorerReviewOperationRisk: {
            check: new simpl_schema_1.default({
                database: {
                    type: String,
                    optional: true
                },
                collection: {
                    type: String
                },
                operation: {
                    type: String,
                    allowedValues: ['insert', 'replace', 'delete', 'command']
                },
                before_document: {
                    type: Object,
                    blackbox: true,
                    optional: true
                },
                after_document: {
                    type: Object,
                    blackbox: true,
                    optional: true
                },
                delete_impact: {
                    type: Object,
                    blackbox: true,
                    optional: true
                },
                permissionView: {
                    type: String,
                    optional: true
                },
                mode: {
                    type: String,
                    optional: true
                }
            }),
            function: function (database, collection, operation, before_document, after_document, delete_impact, permissionView, mode) {
                return __awaiter(this, void 0, void 0, function () {
                    var dbName;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0: return [4 /*yield*/, ensureWriteAccess(this, permissionView, mode)];
                            case 1:
                                _a.sent();
                                dbName = resolveDatabaseName(database);
                                return [2 /*return*/, reviewOperationRisk({
                                        database: dbName,
                                        collection: collection,
                                        operation: operation,
                                        mode: normalizeExplorerMode(mode),
                                        before_document: before_document,
                                        after_document: after_document,
                                        delete_impact: delete_impact
                                    })];
                        }
                    });
                });
            }
        },
        mongoExplorerDeleteDocument: {
            check: new simpl_schema_1.default({
                database: {
                    type: String,
                    optional: true
                },
                collection: {
                    type: String
                },
                doc_id: {
                    type: String
                },
                permissionView: {
                    type: String,
                    optional: true
                },
                force: {
                    type: Boolean,
                    optional: true
                },
                mode: {
                    type: String,
                    optional: true
                }
            }),
            function: function (database_1, collection_1, doc_id_1, permissionView_1) {
                return __awaiter(this, arguments, void 0, function (database, collection, doc_id, permissionView, force, mode) {
                    var dbName, impact, collectionPreview, _a, managerCollection, dbCollection, deleteFilter;
                    if (force === void 0) { force = false; }
                    return __generator(this, function (_b) {
                        switch (_b.label) {
                            case 0: return [4 /*yield*/, ensureWriteAccess(this, permissionView, mode)];
                            case 1:
                                _b.sent();
                                dbName = resolveDatabaseName(database);
                                return [4 /*yield*/, buildDeleteImpact(dbName, collection, doc_id)];
                            case 2:
                                impact = _b.sent();
                                if (!force && (impact.unresolved || impact.has_references)) {
                                    collectionPreview = impact.collections.slice(0, 3).map(function (item) { return item.collection; }).join(', ');
                                    if (impact.unresolved) {
                                        throw new Error('Mongo Explorer: Delete blocked because dependency checks could not complete. Use force delete after review.');
                                    }
                                    throw new Error("Mongo Explorer: Delete blocked. Document is referenced by ".concat(collectionPreview).concat(impact.collections.length > 3 ? ', ...' : ''));
                                }
                                _a = resolveCollectionHandle(dbName, collection), managerCollection = _a.managerCollection, dbCollection = _a.dbCollection;
                                deleteFilter = { _id: doc_id };
                                if (!managerCollection) return [3 /*break*/, 4];
                                return [4 /*yield*/, managerCollection.deleteOne(deleteFilter)];
                            case 3:
                                _b.sent();
                                return [3 /*break*/, 6];
                            case 4: return [4 /*yield*/, dbCollection.deleteOne(deleteFilter)];
                            case 5:
                                _b.sent();
                                _b.label = 6;
                            case 6: return [2 /*return*/, true];
                        }
                    });
                });
            }
        },
        mongoExplorerDeleteImpact: {
            check: new simpl_schema_1.default({
                database: {
                    type: String,
                    optional: true
                },
                collection: {
                    type: String
                },
                doc_id: {
                    type: String
                }
            }),
            function: function (database, collection, doc_id) {
                return __awaiter(this, void 0, void 0, function () {
                    var dbName;
                    return __generator(this, function (_a) {
                        dbName = resolveDatabaseName(database);
                        return [2 /*return*/, buildDeleteImpact(dbName, collection, doc_id)];
                    });
                });
            }
        }
    });
}

//# sourceMappingURL=mongo-explorer.js.map
