import { readEnvString } from '../common/ExternalServiceUtils.js';
import { Logger } from '../config/logger.js';
import { normalizeActiveServiceRuntime, normalizeProjectRuntimeModule, } from '../microservices/ServiceManifest.js';
import { existsSync } from '../node-singletons/fs.js';
import * as path from '../node-singletons/path.js';
import { pathToFileURL } from '../node-singletons/url.js';
const getRuntimeGlobal = () => globalThis;
const getProjectRoot = () => {
    const fromEnv = readEnvString('ZINTRUST_PROJECT_ROOT');
    if (fromEnv.trim().length > 0)
        return fromEnv.trim();
    return process.cwd();
};
const getNodeRuntimeCandidates = (projectRoot) => [
    path.join(projectRoot, 'src', 'zintrust.runtime.ts'),
    path.join(projectRoot, 'dist', 'src', 'zintrust.runtime.js'),
    path.join(projectRoot, 'src', 'zintrust.runtime.js'),
];
const mergeProjectRuntime = (current, next) => {
    return Object.freeze({
        ...(current?.serviceManifest === undefined ? {} : { serviceManifest: current.serviceManifest }),
        ...(current?.activeService === undefined ? {} : { activeService: current.activeService }),
        ...(next.serviceManifest === undefined ? {} : { serviceManifest: next.serviceManifest }),
        ...(next.activeService === undefined ? {} : { activeService: next.activeService }),
    });
};
const cacheProjectRuntime = (module) => {
    const runtimeModule = normalizeProjectRuntimeModule(module);
    const merged = mergeProjectRuntime(getCachedProjectRuntime(), runtimeModule);
    getRuntimeGlobal().__zintrustProjectRuntime = merged;
    return merged;
};
const getCachedProjectRuntime = () => {
    return getRuntimeGlobal().__zintrustProjectRuntime;
};
const hasLoadedServiceManifest = (runtime) => {
    return Array.isArray(runtime?.serviceManifest);
};
const tryImportNodeRuntimeCandidate = async (candidate) => {
    if (!existsSync(candidate))
        return undefined;
    try {
        const moduleUrl = pathToFileURL(candidate).href;
        const runtimeModule = (await import(moduleUrl));
        return cacheProjectRuntime(runtimeModule);
    }
    catch (error) {
        Logger.warn('Failed to import project runtime candidate', {
            candidate,
            error: error instanceof Error ? error.message : String(error),
        });
        return undefined;
    }
};
const tryImportWorkerRuntimeLiteralCandidates = async () => {
    try {
        return cacheProjectRuntime((await import('../zintrust.runtime.wg.js')));
    }
    catch {
        // continue
    }
    try {
        return cacheProjectRuntime((await import('../zintrust.runtime.js')));
    }
    catch {
        // continue
    }
    try {
        return cacheProjectRuntime((await import('../index.js' + 'zintrust.runtime.wg.js')));
    }
    catch {
        // continue
    }
    try {
        return cacheProjectRuntime((await import('../index.js' + 'zintrust.runtime.js')));
    }
    catch {
        return undefined;
    }
};
export const ProjectRuntime = Object.freeze({
    clear() {
        delete getRuntimeGlobal().__zintrustProjectRuntime;
    },
    getCached() {
        return getCachedProjectRuntime();
    },
    set(module) {
        return cacheProjectRuntime(module);
    },
    setActiveService(activeService) {
        const normalized = normalizeActiveServiceRuntime(activeService);
        if (normalized === undefined)
            return getCachedProjectRuntime()?.activeService;
        return cacheProjectRuntime({ activeService: normalized }).activeService;
    },
    async tryLoadNodeRuntime() {
        const cached = getCachedProjectRuntime();
        if (hasLoadedServiceManifest(cached))
            return cached;
        const projectRoot = getProjectRoot();
        const candidates = getNodeRuntimeCandidates(projectRoot);
        for (const candidate of candidates) {
            // eslint-disable-next-line no-await-in-loop
            const loaded = await tryImportNodeRuntimeCandidate(candidate);
            if (loaded !== undefined)
                return loaded;
        }
        return undefined;
    },
    async tryLoadWorkerRuntime() {
        const cached = getCachedProjectRuntime();
        if (hasLoadedServiceManifest(cached))
            return cached;
        return tryImportWorkerRuntimeLiteralCandidates();
    },
    getServiceManifest() {
        return getCachedProjectRuntime()?.serviceManifest ?? [];
    },
    getActiveService() {
        return getCachedProjectRuntime()?.activeService;
    },
});
export default ProjectRuntime;
