/* eslint-disable @typescript-eslint/require-await */
import { Env } from '../../config/env.js';
import { Logger } from '../../config/logger.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import { ProxyCache } from '../adapters/ProxyCache.js';
import { ensureSignedSettings, isRecord, requestSignedProxy, } from '../adapters/SqlProxyAdapterUtils.js';
const normalizeRows = (value) => {
    if (value === null)
        return [];
    if (Array.isArray(value))
        return value;
    return [value];
};
const extractResultPayload = (response) => {
    if (!isRecord(response))
        return response;
    if ('result' in response)
        return response['result'];
    if ('rows' in response)
        return response['rows'];
    return response;
};
const getCacheKey = (collection, operation, args) => {
    return `${collection}:${operation}:${JSON.stringify(args)}`;
};
const resolveProxyUrl = () => {
    const url = Env.get('MONGODB_PROXY_URL', '');
    if (typeof url === 'string' && url.trim() !== '')
        return url;
    const host = Env.get('MONGODB_PROXY_HOST', '127.0.0.1');
    const port = Env.getInt('MONGODB_PROXY_PORT', 8792);
    return `http://${host}:${port}`;
};
const buildProxySettings = () => {
    const baseUrl = resolveProxyUrl();
    const keyId = Env.get('MONGODB_PROXY_KEY_ID', '');
    const secret = Env.get('MONGODB_PROXY_SECRET', '');
    const timeoutMs = Env.getInt('MONGODB_PROXY_TIMEOUT_MS', 30000);
    return { baseUrl, keyId, secret, timeoutMs };
};
const buildSignedProxyConfig = (settings) => ({
    settings,
    missingUrlMessage: 'MongoDB proxy URL is missing',
    missingCredentialsMessage: 'MongoDB proxy signing credentials are missing',
    messages: {
        unauthorized: 'MongoDB proxy unauthorized',
        forbidden: 'MongoDB proxy forbidden',
        rateLimited: 'MongoDB proxy rate limited',
        rejected: 'MongoDB proxy rejected',
        error: 'MongoDB proxy error',
        timedOut: 'MongoDB proxy timed out',
    },
});
const MONGODB_OPERATION_PATH = '/zin/mongodb/operation';
export function createMongoDBProxyAdapter() {
    let connected = false;
    const cache = ProxyCache.create();
    const settings = buildProxySettings();
    return {
        async connect() {
            ensureSignedSettings(buildSignedProxyConfig(settings));
            Logger.info(`Connecting to MongoDB via proxy: ${settings.baseUrl}`);
            connected = true;
        },
        async disconnect() {
            connected = false;
            cache.clear();
            Logger.info('Disconnected from MongoDB proxy');
        },
        async query(_sql, _parameters) {
            if (!connected) {
                throw ErrorFactory.createConnectionError('Not connected to MongoDB proxy');
            }
            // Parse MongoDB-like query (simple implementation)
            // Expected format: collection.operation({...})
            const pattern = /^(\w+)\.(\w+)\((.+)\$/;
            const match = pattern.exec(_sql);
            if (!match) {
                throw ErrorFactory.createGeneralError('Invalid MongoDB query format');
            }
            const [, collection, operation, argsStr] = match;
            const args = JSON.parse(argsStr);
            const cacheKey = getCacheKey(collection, operation, args);
            const cached = cache.get(cacheKey);
            if (cached)
                return cached;
            const response = await requestSignedProxy(buildSignedProxyConfig({
                ...settings,
                signaturePathPrefixToStrip: MONGODB_OPERATION_PATH,
            }), MONGODB_OPERATION_PATH, { collection, operation, args });
            const result = extractResultPayload(response);
            const rows = normalizeRows(result);
            const queryResult = {
                rows,
                rowCount: rows.length,
            };
            cache.set(cacheKey, queryResult);
            return queryResult;
        },
        async queryOne(sql, parameters) {
            const result = await this.query(sql, parameters);
            return result.rows[0] ?? null;
        },
        async ping() {
            if (!connected) {
                throw ErrorFactory.createConnectionError('Not connected to MongoDB proxy');
            }
        },
        async transaction(callback) {
            // MongoDB proxy doesn't support traditional transactions via HTTP
            Logger.warn('MongoDB proxy adapter does not support transactions');
            return callback(this);
        },
        async rawQuery(sql, parameters) {
            const result = await this.query(sql, parameters ?? []);
            return result.rows;
        },
        getType() {
            return 'mongodb-proxy';
        },
        isConnected() {
            return connected;
        },
        getPlaceholder(index) {
            return `$${index}`;
        },
    };
}
