import { Env, ErrorFactory, Logger, SignedRequest } from '../../../src/index.js';
const resolveSigningPrefix = (baseUrl) => {
    try {
        const parsed = new URL(baseUrl);
        const path = parsed.pathname.endsWith('/') ? parsed.pathname.slice(0, -1) : parsed.pathname;
        if (path === '' || path === '/')
            return undefined;
        return path;
    }
    catch {
        return undefined;
    }
};
const buildRequestUrl = (baseUrl, path) => {
    const url = new URL(baseUrl);
    const basePath = url.pathname.endsWith('/') ? url.pathname.slice(0, -1) : url.pathname;
    const requestPath = path.startsWith('/') ? path : `/${path}`;
    url.pathname = `${basePath}${requestPath}`;
    return url;
};
const buildSigningUrl = (requestUrl, baseUrl) => {
    const prefix = resolveSigningPrefix(baseUrl);
    if (!prefix)
        return requestUrl;
    if (requestUrl.pathname === prefix || requestUrl.pathname.startsWith(`${prefix}/`)) {
        const signingUrl = new URL(requestUrl.toString());
        const stripped = requestUrl.pathname.slice(prefix.length);
        signingUrl.pathname = stripped.startsWith('/') ? stripped : `/${stripped}`;
        return signingUrl;
    }
    return requestUrl;
};
const resolveBaseUrl = () => {
    const explicit = Env.get('REDIS_PROXY_URL', '').trim();
    if (explicit !== '')
        return explicit;
    const host = Env.get('REDIS_PROXY_HOST', '127.0.0.1');
    const port = Env.getInt('REDIS_PROXY_PORT', 8791);
    return `http://${host}:${port}`;
};
const buildProxySettings = () => {
    const baseUrl = resolveBaseUrl();
    const rawKeyId = Env.get('REDIS_PROXY_KEY_ID', '').trim();
    const fallbackKeyId = (Env.APP_NAME ?? 'zintrust').trim().toLowerCase().replaceAll(/\s+/g, '_');
    const keyId = (rawKeyId === '' ? fallbackKeyId : rawKeyId) || undefined;
    const secret = Env.get('REDIS_PROXY_SECRET', '') || Env.APP_KEY || undefined;
    const timeoutMs = Env.getInt('REDIS_PROXY_TIMEOUT_MS', Env.ZT_PROXY_TIMEOUT_MS);
    return { baseUrl, keyId, secret, timeoutMs };
};
const buildHeaders = async (settings, requestUrl, body) => {
    const headers = {
        'Content-Type': 'application/json',
    };
    if (settings.keyId && settings.secret) {
        const signingUrl = buildSigningUrl(requestUrl, settings.baseUrl);
        const signed = await SignedRequest.createHeaders({
            method: 'POST',
            url: signingUrl,
            body,
            keyId: settings.keyId,
            secret: settings.secret,
        });
        Object.assign(headers, signed);
    }
    else {
        Logger.warn('[redis-proxy] Proxy signing disabled; sending unsigned request.');
    }
    return headers;
};
const requestProxy = async (settings, path, payload) => {
    if (settings.baseUrl.trim() === '') {
        throw ErrorFactory.createConfigError('Redis proxy URL is missing (REDIS_PROXY_URL)');
    }
    const body = JSON.stringify(payload);
    const url = buildRequestUrl(settings.baseUrl, path);
    const headers = await buildHeaders(settings, url, body);
    const timeoutSignal = typeof AbortSignal !== 'undefined' && 'timeout' in AbortSignal;
    const signal = timeoutSignal ? AbortSignal.timeout(settings.timeoutMs) : undefined;
    const response = await fetch(url.toString(), {
        method: 'POST',
        headers,
        body,
        signal,
    });
    if (!response.ok) {
        const text = await response.text();
        throw ErrorFactory.createTryCatchError(`Redis proxy request failed (${response.status})`, text);
    }
    return (await response.json());
};
const toNumber = (value) => {
    if (typeof value === 'number')
        return value;
    if (typeof value === 'string') {
        const parsed = Number(value);
        return Number.isFinite(parsed) ? parsed : 0;
    }
    return 0;
};
export const RedisProxyAdapter = Object.freeze({
    create() {
        const settings = buildProxySettings();
        const sendCommand = async (command, args) => {
            const response = await requestProxy(settings, '/zin/redis/command', {
                command,
                args,
            });
            return response.result;
        };
        return {
            async get(key) {
                const result = await sendCommand('GET', [key]);
                if (result === null)
                    return null;
                try {
                    return JSON.parse(result);
                }
                catch {
                    return null;
                }
            },
            async set(key, value, ttl) {
                const json = JSON.stringify(value);
                if (Number.isFinite(ttl) && (ttl ?? 0) > 0) {
                    await sendCommand('SET', [key, json, 'EX', ttl]);
                }
                else {
                    await sendCommand('SET', [key, json]);
                }
            },
            async delete(key) {
                await sendCommand('DEL', [key]);
            },
            async clear() {
                await sendCommand('FLUSHDB', []);
            },
            async has(key) {
                const result = await sendCommand('EXISTS', [key]);
                return toNumber(result) > 0;
            },
        };
    },
});
export default RedisProxyAdapter;
