"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MariaDBInstrumentation = void 0;
function __assignType(fn, args) {
    fn.__type = args;
    return fn;
}
const instrumentation_1 = require("@opentelemetry/instrumentation");
const AttributeNames_1 = require("@opentelemetry/instrumentation-mysql/build/src/AttributeNames");
const utils_1 = require("@opentelemetry/instrumentation-mysql/build/src/utils");
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
const helpers_1 = require("./helpers");
class MariaDBInstrumentation extends instrumentation_1.InstrumentationBase {
    constructor(config) {
        super('@opentelemetry/instrumentation-mariadb', '0.0.1', config ?? {});
        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
        this._originals = (Map.Ω = [['&'], [() => Function, 'Pu!']], new Map());
        this._setMetricInstruments();
    }
    setMeterProvider(meterProvider) {
        super.setMeterProvider(meterProvider);
        this._setMetricInstruments();
    }
    _setMetricInstruments() {
        this._connectionsUsage = this.meter.createUpDownCounter('db.client.connections.usage', //TODO:: use semantic convention
        {
            description: 'The number of connections that are currently in state described by the state attribute.',
            unit: '{connection}'
        });
    }
    // mariadb 3.5+ uses ESM, where exports are writable but not configurable.
    // shimmer's _wrap uses Object.defineProperty which requires configurable,
    // so we fall back to direct assignment for non-configurable properties.
    _wrapCompat(moduleExports, name, 
    // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
    wrapper) {
        const descriptor = Object.getOwnPropertyDescriptor(moduleExports, name);
        // oxlint-disable-next-line typescript/no-unsafe-function-type
        const original = moduleExports[name];
        if (descriptor && !descriptor.configurable) {
            this._originals.set(name, original);
            // oxlint-disable-next-line typescript/no-explicit-any
            moduleExports[name] = wrapper(original);
        }
        else {
            // oxlint-disable-next-line typescript/no-explicit-any
            this._wrap(moduleExports, name, wrapper);
        }
    }
    _unwrapCompat(moduleExports, name) {
        const original = this._originals.get(name);
        if (original) {
            // oxlint-disable-next-line typescript/no-explicit-any
            moduleExports[name] = original;
            this._originals.delete(name);
        }
        else {
            this._unwrap(moduleExports, name);
        }
    }
    init() {
        return [
            new instrumentation_1.InstrumentationNodeModuleDefinition('mariadb', ['3.*'], __assignType(moduleExports => {
                // ESM namespace objects are sealed (non-configurable, non-writable).
                // Create a mutable shallow copy so we can patch the exports.
                if (!Object.isExtensible(moduleExports)) {
                    moduleExports = { ...moduleExports };
                }
                if ((0, instrumentation_1.isWrapped)(moduleExports.createConnection)) {
                    this._unwrapCompat(moduleExports, 'createConnection');
                }
                this._wrapCompat(moduleExports, 'createConnection', this._patchCreateConnection());
                if ((0, instrumentation_1.isWrapped)(moduleExports.createPool)) {
                    this._unwrapCompat(moduleExports, 'createPool');
                }
                this._wrapCompat(moduleExports, 'createPool', this._patchCreatePool());
                if ((0, instrumentation_1.isWrapped)(moduleExports.createPoolCluster)) {
                    this._unwrapCompat(moduleExports, 'createPoolCluster');
                }
                this._wrapCompat(moduleExports, 'createPoolCluster', this._patchCreatePoolCluster());
                return moduleExports;
            }, ['moduleExports', '', 'P"2!"/"']), __assignType(moduleExports => {
                if (moduleExports === undefined)
                    return;
                this._unwrapCompat(moduleExports, 'createConnection');
                this._unwrapCompat(moduleExports, 'createPool');
                this._unwrapCompat(moduleExports, 'createPoolCluster');
            }, ['moduleExports', '', 'P"2!"/"']))
        ];
    }
    // global export function
    _patchCreateConnection() {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
        return __assignType((originalCreateConnection) => {
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            const thisPlugin = this;
            return __assignType(function createConnection(_connectionUri) {
                // eslint-disable-next-line prefer-rest-params
                const originalResult = originalCreateConnection(...arguments);
                // This is unwrapped on next call after unpatch
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                thisPlugin._wrap(originalResult, 'query', thisPlugin._patchQuery(originalResult));
                return originalResult;
            }, ['_connectionUri', 'createConnection', 'PP&!J2!"/"']);
        }, [() => Function, 'originalCreateConnection', '', 'PPu!2""/#']);
    }
    // global export function
    _patchCreatePool() {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
        return __assignType((originalCreatePool) => {
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            const thisPlugin = this;
            return __assignType(function createPool(_config) {
                // eslint-disable-next-line prefer-rest-params
                const pool = originalCreatePool(...arguments);
                thisPlugin._wrap(pool, 'query', thisPlugin._patchQuery(pool));
                thisPlugin._wrap(pool, 'getConnection', thisPlugin._patchGetConnection(pool));
                thisPlugin._wrap(pool, 'end', thisPlugin._patchPoolEnd(pool));
                thisPlugin._setPoolcallbacks(pool, thisPlugin, '');
                return pool;
            }, ['_config', 'createPool', 'PP&!J2!"/"']);
        }, [() => Function, 'originalCreatePool', '', 'PPu!2""/#']);
    }
    _patchPoolEnd(pool) {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
        return __assignType((originalPoolEnd) => {
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            const thisPlugin = this;
            return __assignType(function end(_callback) {
                const nAll = pool.totalConnections();
                const nFree = pool.idleConnections();
                const nUsed = nAll - nFree;
                const poolName = 'unk'; // todo
                thisPlugin._connectionsUsage.add(-nUsed, {
                    state: 'used',
                    name: poolName
                });
                thisPlugin._connectionsUsage.add(-nFree, {
                    state: 'idle',
                    name: poolName
                });
                // eslint-disable-next-line prefer-rest-params
                return originalPoolEnd.apply(pool, arguments);
            }, ['_callback', 'end', 'P#2!8"/"']);
        }, [() => Function, 'originalPoolEnd', '', 'PPu!2""/#']);
    }
    // global export function
    _patchCreatePoolCluster() {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
        return __assignType((originalCreatePoolCluster) => {
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            const thisPlugin = this;
            return __assignType(function createPool(_config) {
                // eslint-disable-next-line prefer-rest-params
                const cluster = originalCreatePoolCluster(...arguments);
                // This is unwrapped on next call after unpatch
                thisPlugin._wrap(cluster, 'getConnection', thisPlugin._patchGetConnection(cluster));
                thisPlugin._wrap(cluster, 'add', thisPlugin._patchAdd(cluster));
                return cluster;
            }, ['_config', 'createPool', 'PP&!J2!"/"']);
        }, [() => Function, 'originalCreatePoolCluster', '', 'PPu!2""/#']);
    }
    _patchAdd(cluster) {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
        return __assignType((originalAdd) => {
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            const thisPlugin = this;
            return __assignType(function add(id, _config) {
                // Unwrap if unpatch has been called
                if (!thisPlugin['_enabled']) {
                    thisPlugin._unwrap(cluster, 'add');
                    // eslint-disable-next-line prefer-rest-params
                    return originalAdd.apply(cluster, arguments);
                }
                // eslint-disable-next-line prefer-rest-params
                originalAdd.apply(cluster, arguments);
                // eslint-disable-next-line @typescript-eslint/no-explicit-any
                const nodes = cluster['_nodes'];
                if (nodes) {
                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
                    const nodeId = typeof id === 'object' ? 'CLUSTER::' + cluster._lastId : String(id);
                    const pool = nodes[nodeId].pool;
                    thisPlugin._setPoolcallbacks(pool, thisPlugin, id);
                }
            }, ['id', '_config', 'add', 'P&2!#2""/#']);
        }, [() => Function, 'originalAdd', '', 'PPu!2""/#']);
    }
    // method on cluster or pool
    _patchGetConnection(pool) {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
        return __assignType((originalGetConnection) => {
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            const thisPlugin = this;
            return __assignType(async function getConnection() {
                // Unwrap if unpatch has been called
                if (!thisPlugin['_enabled']) {
                    thisPlugin._unwrap(pool, 'getConnection');
                    // eslint-disable-next-line prefer-rest-params
                    return originalGetConnection.apply(pool, arguments);
                }
                // eslint-disable-next-line prefer-rest-params
                const connection = await originalGetConnection.apply(pool, arguments);
                if (!(0, instrumentation_1.isWrapped)(connection.query)) {
                    thisPlugin._wrap(connection, 'query', thisPlugin._patchQuery(connection));
                }
                return connection;
            }, ['getConnection', 'P"/!']);
        }, [() => Function, 'originalGetConnection', '', 'PPu!2""/#']);
    }
    _patchQuery(connection) {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
        return __assignType((originalQuery) => {
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            const thisPlugin = this;
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            return __assignType(function query(query, _valuesOrCallback) {
                if (!thisPlugin['_enabled']) {
                    thisPlugin._unwrap(connection, 'query');
                    // eslint-disable-next-line prefer-rest-params
                    return originalQuery.apply(connection, arguments);
                }
                query = query.trim();
                const spanName = (0, utils_1.getSpanName)(query);
                const spanAttributes = {
                    ...MariaDBInstrumentation.COMMON_ATTRIBUTES,
                    [semantic_conventions_1.ATTR_DB_QUERY_TEXT]: (0, utils_1.getDbQueryText)(query)
                };
                const instrumentationConfig = thisPlugin.getConfig();
                if (instrumentationConfig.enhancedDatabaseReporting) {
                    let values;
                    if (Array.isArray(_valuesOrCallback)) {
                        values = _valuesOrCallback;
                        // eslint-disable-next-line prefer-rest-params
                    }
                    else if (arguments[2]) {
                        values = [_valuesOrCallback];
                    }
                    spanAttributes[AttributeNames_1.AttributeNames.MYSQL_VALUES] = (0, utils_1.getDbValues)(query, values);
                }
                return (0, helpers_1.withSpan)(spanName, spanAttributes, () => originalQuery.apply(connection, [query, _valuesOrCallback]));
            }, ['query', '_valuesOrCallback', 'P&2!"F2"8"/!']);
        }, [() => Function, 'originalQuery', '', 'PPu!2""/#']);
    }
    _setPoolcallbacks(pool, thisPlugin, id) {
        //TODO:: use semantic convention
        const poolName = id || 'unk';
        pool.on('connection', __assignType(_connection => {
            thisPlugin._connectionsUsage.add(1, {
                state: 'idle',
                name: poolName
            });
        }, ['_connection', '', 'P"2!"/"']));
        pool.on('acquire', __assignType(_connection => {
            thisPlugin._connectionsUsage.add(-1, {
                state: 'idle',
                name: poolName
            });
            thisPlugin._connectionsUsage.add(1, {
                state: 'used',
                name: poolName
            });
        }, ['_connection', '', 'P"2!"/"']));
        pool.on('release', __assignType(_connection => {
            thisPlugin._connectionsUsage.add(-1, {
                state: 'used',
                name: poolName
            });
            thisPlugin._connectionsUsage.add(1, {
                state: 'idle',
                name: poolName
            });
        }, ['_connection', '', 'P"2!"/"']));
    }
}
exports.MariaDBInstrumentation = MariaDBInstrumentation;
MariaDBInstrumentation.COMMON_ATTRIBUTES = {
    [semantic_conventions_1.ATTR_DB_SYSTEM_NAME]: semantic_conventions_1.DB_SYSTEM_NAME_VALUE_MARIADB
};
MariaDBInstrumentation.__type = [() => instrumentation_1.InstrumentationBase, 'COMMON_ATTRIBUTES', function () { return {
        [semantic_conventions_1.ATTR_DB_SYSTEM_NAME]: semantic_conventions_1.DB_SYSTEM_NAME_VALUE_MARIADB
    }; }, 'UpDownCounter', '_connectionsUsage', 'MySQLInstrumentationConfig', 'config', 'constructor', 'MeterProvider', 'meterProvider', 'setMeterProvider', '_setMetricInstruments', '_originals', function () { return (Map.Ω = [['&'], [() => Function, 'Pu!']], new Map()); }, () => mariadbTypes, 'moduleExports', 'name', () => Function, 'original', () => Function, '', 'wrapper', '_wrapCompat', () => mariadbTypes, '_unwrapCompat', 'init', '_patchCreateConnection', '_patchCreatePool', 'pool', '_patchPoolEnd', '_patchCreatePoolCluster', 'cluster', '_patchAdd', '_patchGetConnection', 'connection', '_patchQuery', () => MariaDBInstrumentation, 'thisPlugin', 'id', '_setPoolcallbacks', 'MariaDBInstrumentation', 'P7!!3"9s>#"w$3%;P"w&2\'8"0(P"w)2*"0+P"0,;!3-;>.Pi/20"21PPu223Pu4/526"07;Pi820"21"09;P"0:<P"0;;P"0<;P!2="0>;P"0?;P!2@"0A;PP!!J2="0B;PP!!J2C"0D;P!2=P7E2F&2G"0H;5wI'];
//# sourceMappingURL=MariaDBInstrumentation.js.map