/**
 * Runtime Services
 * Adapter layer for env/crypto/timers/fs/fetch across Node and Workers
 * Sealed namespace for immutability
 */
import { Cloudflare } from '../config/cloudflare.js';
import { Env } from '../config/env.js';
import { ErrorFactory } from '../exceptions/ZintrustError.js';
import { randomUUID as nodeRandomUUID, randomBytes } from '../node-singletons/crypto.js';
import * as nodeFs from '../node-singletons/fs.js';
export const detectCloudflareWorkers = () => {
    return Cloudflare.getWorkersEnv() !== null;
};
export const detectRuntimePlatform = () => {
    if (detectCloudflareWorkers())
        return 'cloudflare';
    return 'nodejs';
};
export const RUNTIME_PLATFORM = detectRuntimePlatform();
const normalizeEnvValue = (value) => {
    if (value === null || value === undefined)
        return '';
    if (typeof value === 'string')
        return value;
    if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
        return String(value);
    }
    return '';
};
const readEnvFromRecord = (record, key) => {
    return normalizeEnvValue(record[key]);
};
const createWorkersEnvReader = () => {
    return {
        get(key, defaultValue = '') {
            const env = Cloudflare.getWorkersEnv() ?? {};
            const value = readEnvFromRecord(env, key);
            return value === '' ? defaultValue : value;
        },
        getInt(key, defaultValue = 0) {
            const raw = this.get(key, String(defaultValue));
            const parsed = Number.parseInt(raw, 10);
            return Number.isFinite(parsed) ? parsed : defaultValue;
        },
        getFloat(key, defaultValue = 0) {
            const raw = this.get(key, String(defaultValue));
            const parsed = Number.parseFloat(raw);
            return Number.isFinite(parsed) ? parsed : defaultValue;
        },
        getBool(key, defaultValue = false) {
            const raw = this.get(key, defaultValue ? 'true' : 'false').toLowerCase();
            if (raw === '')
                return defaultValue;
            return raw === 'true' || raw === '1';
        },
    };
};
const createNodeEnvReader = () => {
    return {
        get: (key, defaultValue) => Env.get(key, defaultValue),
        getInt: (key, defaultValue) => Env.getInt(key, defaultValue ?? 0),
        getFloat: (key, defaultValue) => Env.getFloat(key, defaultValue ?? 0),
        getBool: (key, defaultValue) => Env.getBool(key, defaultValue),
    };
};
const getWebCrypto = () => {
    if (globalThis.crypto === undefined) {
        throw ErrorFactory.createConfigError('WebCrypto is not available in this runtime');
    }
    return globalThis.crypto;
};
const fillRandomValues = (webCrypto, array) => {
    if (typeof webCrypto.getRandomValues === 'function') {
        return webCrypto.getRandomValues(array);
    }
    if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
        throw ErrorFactory.createConfigError('BigInt typed arrays require WebCrypto support');
    }
    const bytes = randomBytes(array.byteLength);
    const view = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
    view.set(bytes);
    return array;
};
const createNodeCrypto = () => {
    const webCrypto = getWebCrypto();
    if (webCrypto.subtle === undefined) {
        throw ErrorFactory.createConfigError('WebCrypto subtle is not available in Node runtime');
    }
    return {
        subtle: webCrypto.subtle,
        getRandomValues: (array) => {
            return fillRandomValues(webCrypto, array);
        },
        randomUUID: () => {
            if (typeof webCrypto.randomUUID === 'function')
                return webCrypto.randomUUID();
            return nodeRandomUUID();
        },
        randomBytes: (size) => randomBytes(size),
    };
};
const createWorkersCrypto = () => {
    const webCrypto = getWebCrypto();
    if (webCrypto.subtle === undefined) {
        throw ErrorFactory.createConfigError('WebCrypto subtle is not available in Workers runtime');
    }
    return {
        subtle: webCrypto.subtle,
        getRandomValues: (array) => webCrypto.getRandomValues(array),
        randomUUID: () => {
            if (typeof webCrypto.randomUUID === 'function')
                return webCrypto.randomUUID();
            return nodeRandomUUID();
        },
    };
};
const createNodeFs = () => {
    return {
        supported: true,
        readFileSync: (path, encoding) => nodeFs.readFileSync(path, encoding),
        readdirSync: (path) => nodeFs.readdirSync(path),
        existsSync: (path) => nodeFs.existsSync(path),
    };
};
const createWorkersFs = () => {
    const unsupported = () => {
        throw ErrorFactory.createConfigError('Filesystem access is not supported in Workers runtime');
    };
    return {
        supported: false,
        readFileSync: () => unsupported(),
        readdirSync: () => unsupported(),
        existsSync: () => false,
    };
};
const createTimers = () => ({
    setTimeout: globalThis.setTimeout.bind(globalThis),
    clearTimeout: globalThis.clearTimeout.bind(globalThis),
    setInterval: globalThis.setInterval.bind(globalThis),
    clearInterval: globalThis.clearInterval.bind(globalThis),
});
export const RuntimeServices = Object.freeze({
    create(platform) {
        if (platform === 'cloudflare') {
            return {
                platform,
                env: createWorkersEnvReader(),
                crypto: createWorkersCrypto(),
                timers: createTimers(),
                fs: createWorkersFs(),
                fetch: globalThis.fetch.bind(globalThis),
            };
        }
        return {
            platform,
            env: createNodeEnvReader(),
            crypto: createNodeCrypto(),
            timers: createTimers(),
            fs: createNodeFs(),
            fetch: globalThis.fetch.bind(globalThis),
        };
    },
});
export default RuntimeServices;
