"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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
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 __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.GqlStatusObject = exports.Notification = exports.notificationClassification = exports.notificationCategory = exports.notificationSeverityLevel = void 0;
exports.polyfillGqlStatusObject = polyfillGqlStatusObject;
exports.polyfillNotification = polyfillNotification;
exports.buildGqlStatusObjectFromMetadata = buildGqlStatusObjectFromMetadata;
exports.buildNotificationsFromMetadata = buildNotificationsFromMetadata;
/**
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var json = __importStar(require("./json"));
var internal_1 = require("./internal");
var gql_constants_1 = require("./gql-constants");
var unknownGqlStatus = {
    WARNING: {
        gql_status: '01N42',
        status_description: 'warn: unknown warning'
    },
    NO_DATA: {
        gql_status: '02N42',
        status_description: 'note: no data - unknown subcondition'
    },
    INFORMATION: {
        gql_status: '03N42',
        status_description: 'info: unknown notification'
    },
    ERROR: {
        gql_status: '50N42',
        status_description: 'error: general processing exception - unexpected error'
    }
};
/**
 * @typedef {'WARNING' | 'INFORMATION' | 'UNKNOWN'} NotificationSeverityLevel
 */
/**
 * Constants that represents the Severity level in the {@link Notification}
 */
var notificationSeverityLevel = {
    WARNING: 'WARNING',
    INFORMATION: 'INFORMATION',
    UNKNOWN: 'UNKNOWN'
};
exports.notificationSeverityLevel = notificationSeverityLevel;
Object.freeze(notificationSeverityLevel);
var severityLevels = Object.values(notificationSeverityLevel);
/**
 * @typedef {'HINT' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | 'TOPOLOGY' | 'SECURITY' | 'DEPRECATION' | 'GENERIC' | 'SCHEMA' | 'UNKNOWN' } NotificationCategory
 */
/**
 * Constants that represents the Category in the {@link Notification}
 */
var notificationCategory = {
    HINT: 'HINT',
    UNRECOGNIZED: 'UNRECOGNIZED',
    UNSUPPORTED: 'UNSUPPORTED',
    PERFORMANCE: 'PERFORMANCE',
    DEPRECATION: 'DEPRECATION',
    TOPOLOGY: 'TOPOLOGY',
    SECURITY: 'SECURITY',
    GENERIC: 'GENERIC',
    SCHEMA: 'SCHEMA',
    UNKNOWN: 'UNKNOWN'
};
exports.notificationCategory = notificationCategory;
Object.freeze(notificationCategory);
var categories = Object.values(notificationCategory);
/**
 * @typedef {NotificationCategory} NotificationClassification
 * @experimental
 */
/**
 * Constants that represents the Classification in the {@link GqlStatusObject}
 * @type {notificationCategory}
 * @experimental
 */
var notificationClassification = notificationCategory;
exports.notificationClassification = notificationClassification;
/**
 * Class for Cypher notifications
 * @access public
 */
var Notification = /** @class */ (function () {
    /**
     * Create a Notification instance
     * @constructor
     * @param {Object} notification - Object with notification data
     */
    function Notification(notification) {
        /**
         * The code
         * @type {string}
         * @public
         */
        this.code = notification.code;
        /**
         * The title
         * @type {string}
         * @public
         */
        this.title = notification.title;
        /**
         * The description
         * @type {string}
         * @public
         */
        this.description = notification.description;
        /**
         * The raw severity
         *
         * Use {@link Notification#rawSeverityLevel} for the raw value or {@link Notification#severityLevel} for an enumerated value.
         *
         * @type {string}
         * @public
         * @deprecated This property will be removed in 6.0.
         */
        this.severity = notification.severity;
        /**
         * The position which the notification had occur.
         *
         * @type {NotificationPosition}
         * @public
         */
        this.position = _constructPosition(notification.position);
        /**
         * The severity level
         *
         * @type {NotificationSeverityLevel}
         * @public
         * @example
         * const { summary } = await session.run("RETURN 1")
         *
         * for (const notification of summary.notifications) {
         *     switch(notification.severityLevel) {
         *         case neo4j.notificationSeverityLevel.INFORMATION: // or simply 'INFORMATION'
         *             console.info(`${notification.title} - ${notification.description}`)
         *             break
         *         case neo4j.notificationSeverityLevel.WARNING: // or simply 'WARNING'
         *             console.warn(`${notification.title} - ${notification.description}`)
         *             break
         *         case neo4j.notificationSeverityLevel.UNKNOWN: // or simply 'UNKNOWN'
         *         default:
         *             // the raw info came from the server could be found at notification.rawSeverityLevel
         *             console.log(`${notification.title} - ${notification.description}`)
         *             break
         *     }
         * }
         */
        this.severityLevel = _asEnumerableSeverity(notification.severity);
        /**
         * The severity level returned by the server without any validation.
         *
         * @type {string}
         * @public
         */
        this.rawSeverityLevel = notification.severity;
        /**
         * The category
         *
         * @type {NotificationCategory}
         * @public
         * @example
         * const { summary } = await session.run("RETURN 1")
         *
         * for (const notification of summary.notifications) {
         *     switch(notification.category) {
         *         case neo4j.notificationCategory.QUERY: // or simply 'QUERY'
         *             console.info(`${notification.title} - ${notification.description}`)
         *             break
         *         case neo4j.notificationCategory.PERFORMANCE: // or simply 'PERFORMANCE'
         *             console.warn(`${notification.title} - ${notification.description}`)
         *             break
         *         case neo4j.notificationCategory.UNKNOWN: // or simply 'UNKNOWN'
         *         default:
         *             // the raw info came from the server could be found at notification.rawCategory
         *             console.log(`${notification.title} - ${notification.description}`)
         *             break
         *     }
         * }
         */
        this.category = _asEnumerableClassification(notification.category);
        /**
         * The category returned by the server without any validation.
         *
         * @type {string|undefined}
         * @public
         */
        this.rawCategory = notification.category;
    }
    return Notification;
}());
exports.Notification = Notification;
/**
 * Representation for GqlStatusObject found when executing a query.
 * <p>
 * This object represents a status of query execution.
 * This status is a superset of {@link Notification}.
 *
 * @experimental
 * @public
 */
var GqlStatusObject = /** @class */ (function () {
    /**
     *
     * @param rawGqlStatusObject
     * @private
     */
    function GqlStatusObject(rawGqlStatusObject) {
        var _a;
        /**
         * The GQLSTATUS
         *
         * @type {string}
         * @public
         */
        this.gqlStatus = rawGqlStatusObject.gql_status;
        /**
         * The GQLSTATUS description
         *
         * @type {string}
         * @public
         */
        this.statusDescription = rawGqlStatusObject.status_description;
        /**
         * The diagnostic record as it is.
         *
         * @type {object}
         * @public
         */
        this.diagnosticRecord = (_a = rawGqlStatusObject.diagnostic_record) !== null && _a !== void 0 ? _a : {};
        /**
         * The position at which the notification had occurred.
         *
         * @type {NotificationPosition | undefined}
         * @public
         */
        this.position = this.diagnosticRecord._position != null ? _constructPosition(this.diagnosticRecord._position) : undefined;
        /**
         * The severity
         *
         * @type {NotificationSeverityLevel}
         * @public
         * @example
         * const { summary } = await session.run("RETURN 1")
         *
         * for (const gqlStatusObject of summary.gqlStatusObjects) {
         *     switch(gqlStatusObject.severity) {
         *         case neo4j.notificationSeverityLevel.INFORMATION: // or simply 'INFORMATION'
         *             console.info(gqlStatusObject.statusDescription)
         *             break
         *         case neo4j.notificationSeverityLevel.WARNING: // or simply 'WARNING'
         *             console.warn(gqlStatusObject.statusDescription)
         *             break
         *         case neo4j.notificationSeverityLevel.UNKNOWN: // or simply 'UNKNOWN'
         *         default:
         *             // the raw info came from the server could be found at gqlStatusObject.rawSeverity
         *             console.log(gqlStatusObject.statusDescription)
         *             break
         *     }
         * }
         */
        this.severity = _asEnumerableSeverity(this.diagnosticRecord._severity);
        /**
         * The severity returned in the diagnostic record from the server without any validation.
         *
         * @type {string | undefined}
         * @public
         */
        this.rawSeverity = this.diagnosticRecord._severity;
        /**
         * The classification
         *
         * @type {NotificationClassification}
         * @public
         * @example
         * const { summary } = await session.run("RETURN 1")
         *
         * for (const gqlStatusObject of summary.gqlStatusObjects) {
         *     switch(gqlStatusObject.classification) {
         *         case neo4j.notificationClassification.QUERY: // or simply 'QUERY'
         *             console.info(gqlStatusObject.statusDescription)
         *             break
         *         case neo4j.notificationClassification.PERFORMANCE: // or simply 'PERFORMANCE'
         *             console.warn(gqlStatusObject.statusDescription)
         *             break
         *         case neo4j.notificationClassification.UNKNOWN: // or simply 'UNKNOWN'
         *         default:
         *             // the raw info came from the server can be found at notification.rawCategory
         *             console.log(gqlStatusObject.statusDescription)
         *             break
         *     }
         * }
         */
        this.classification = _asEnumerableClassification(this.diagnosticRecord._classification);
        /**
         * The category returned by the server without any validation.
         *
         * @type {string|undefined}
         * @public
         */
        this.rawClassification = this.diagnosticRecord._classification;
        /**
         * Indicates if this object represents a notification and it can be filtered using
         * NotificationFilter.
         *
         * Only GqlStatusObject which is Notification has meaningful position, severity and
         * classification.
         *
         * @type {boolean}
         * @public
         */
        this.isNotification = rawGqlStatusObject.neo4j_code != null;
        Object.freeze(this);
    }
    Object.defineProperty(GqlStatusObject.prototype, "diagnosticRecordAsJsonString", {
        /**
         * The json string representation of the diagnostic record.
         * The goal of this method is provide a serialized object for human inspection.
         *
         * @type {string}
         * @public
         */
        get: function () {
            return json.stringify(this.diagnosticRecord, { useCustomToString: true });
        },
        enumerable: false,
        configurable: true
    });
    return GqlStatusObject;
}());
exports.GqlStatusObject = GqlStatusObject;
/**
 *
 * @private
 * @param status
 * @returns {Notification|undefined}
 */
function polyfillNotification(status) {
    var _a, _b, _c;
    // Non notification status should have neo4j_code
    if (status.neo4j_code == null) {
        return undefined;
    }
    return new Notification({
        code: status.neo4j_code,
        title: status.title,
        description: status.description,
        severity: (_a = status.diagnostic_record) === null || _a === void 0 ? void 0 : _a._severity,
        category: (_b = status.diagnostic_record) === null || _b === void 0 ? void 0 : _b._classification,
        position: (_c = status.diagnostic_record) === null || _c === void 0 ? void 0 : _c._position
    });
}
/**
 * @private
 * @param notification
 * @returns {GqlStatusObject}
 */
function polyfillGqlStatusObject(notification) {
    var _a;
    var defaultStatus = notification.severity === notificationSeverityLevel.WARNING ? unknownGqlStatus.WARNING : unknownGqlStatus.INFORMATION;
    var polyfilledRawObj = {
        gql_status: defaultStatus.gql_status,
        status_description: (_a = notification.description) !== null && _a !== void 0 ? _a : defaultStatus.status_description,
        neo4j_code: notification.code,
        title: notification.title,
        diagnostic_record: __assign({}, gql_constants_1.rawPolyfilledDiagnosticRecord)
    };
    if (notification.severity != null) {
        polyfilledRawObj.diagnostic_record._severity = notification.severity;
    }
    if (notification.category != null) {
        polyfilledRawObj.diagnostic_record._classification = notification.category;
    }
    if (notification.position != null) {
        polyfilledRawObj.diagnostic_record._position = notification.position;
    }
    return new GqlStatusObject(polyfilledRawObj);
}
/**
 * This objects are used for polyfilling the first status on the status list
 *
 * @private
 */
var staticGqlStatusObjects = {
    SUCCESS: new GqlStatusObject({
        gql_status: '00000',
        status_description: 'note: successful completion',
        diagnostic_record: gql_constants_1.rawPolyfilledDiagnosticRecord
    }),
    NO_DATA: new GqlStatusObject({
        gql_status: '02000',
        status_description: 'note: no data',
        diagnostic_record: gql_constants_1.rawPolyfilledDiagnosticRecord
    }),
    NO_DATA_UNKNOWN_SUBCONDITION: new GqlStatusObject(__assign(__assign({}, unknownGqlStatus.NO_DATA), { diagnostic_record: gql_constants_1.rawPolyfilledDiagnosticRecord })),
    OMITTED_RESULT: new GqlStatusObject({
        gql_status: '00001',
        status_description: 'note: successful completion - omitted result',
        diagnostic_record: gql_constants_1.rawPolyfilledDiagnosticRecord
    })
};
Object.freeze(staticGqlStatusObjects);
/**
 *
 * @private
 * @param metadata
 * @returns
 */
function buildGqlStatusObjectFromMetadata(metadata) {
    var _a, _b;
    function getGqlStatusObjectFromStreamSummary(summary) {
        if ((summary === null || summary === void 0 ? void 0 : summary.have_records_streamed) === true) {
            return staticGqlStatusObjects.SUCCESS;
        }
        if ((summary === null || summary === void 0 ? void 0 : summary.has_keys) === false) {
            return staticGqlStatusObjects.OMITTED_RESULT;
        }
        if ((summary === null || summary === void 0 ? void 0 : summary.pulled) === true) {
            return staticGqlStatusObjects.NO_DATA;
        }
        return staticGqlStatusObjects.NO_DATA_UNKNOWN_SUBCONDITION;
    }
    if (metadata.statuses != null) {
        return metadata.statuses.map(function (status) { return new GqlStatusObject(status); });
    }
    var clientGenerated = getGqlStatusObjectFromStreamSummary(metadata.stream_summary);
    var polyfilledObjects = __spreadArray([clientGenerated], __read(((_b = (_a = metadata.notifications) === null || _a === void 0 ? void 0 : _a.map(polyfillGqlStatusObject)) !== null && _b !== void 0 ? _b : [])), false);
    return polyfilledObjects.sort(function (a, b) { return calculateWeight(a) - calculateWeight(b); });
}
var gqlStatusWeightByClass = Object.freeze({
    '02': 0,
    '01': 1,
    '00': 2
});
/**
 * GqlStatus weight
 *
 * @private
 */
function calculateWeight(gqlStatusObject) {
    var _a, _b;
    var gqlClass = (_a = gqlStatusObject.gqlStatus) === null || _a === void 0 ? void 0 : _a.slice(0, 2);
    // @ts-expect-error
    return (_b = gqlStatusWeightByClass[gqlClass]) !== null && _b !== void 0 ? _b : 9999;
}
/**
 *
 * @private
 * @param metadata
 * @returns
 */
function buildNotificationsFromMetadata(metadata) {
    if (metadata.notifications != null) {
        return metadata.notifications.map(function (n) { return new Notification(n); });
    }
    if (metadata.statuses != null) {
        return metadata.statuses.map(polyfillNotification).filter(function (n) { return n != null; });
    }
    return [];
}
/**
 *
 * @private
 * @param pos
 * @returns {NotificationPosition}
 */
function _constructPosition(pos) {
    if (pos == null) {
        return {};
    }
    /* eslint-disable @typescript-eslint/no-non-null-assertion */
    return {
        offset: internal_1.util.toNumber(pos.offset),
        line: internal_1.util.toNumber(pos.line),
        column: internal_1.util.toNumber(pos.column)
    };
    /* eslint-enable @typescript-eslint/no-non-null-assertion */
}
function _asEnumerableSeverity(severity) {
    return severityLevels.includes(severity)
        ? severity
        : notificationSeverityLevel.UNKNOWN;
}
function _asEnumerableClassification(classification) {
    return categories.includes(classification)
        ? classification
        : notificationClassification.UNKNOWN;
}
exports.default = Notification;
