import { ExecutionContextFactory } from '../../connector/execution/factory.js';
import { toErrorMessage } from '../../utils/error.js';
import { getRootLogger } from '../../utils/logger.js';
import { ConnectorManager } from './lifecycle.js';
export class EnvironmentConnectorManager {
    environments = new Map();
    connectorRegistry;
    scopedRegistry;
    credentialVault;
    listeners = [];
    log;
    constructor(config) {
        this.connectorRegistry = config.connectorRegistry;
        this.scopedRegistry = config.scopedRegistry;
        this.credentialVault = config.credentialVault;
        this.log = getRootLogger().child({ component: 'EnvironmentConnectorManager' });
    }
    registerEnvironment(setup) {
        const envId = setup.environment.id;
        if (this.environments.has(envId)) {
            this.log.warn(`Environment "${envId}" already registered, skipping.`);
            return;
        }
        const manager = new ConnectorManager({ registry: this.connectorRegistry });
        manager.on((event) => this.emit(event));
        let executionContext;
        if (setup.executionContext) {
            executionContext = ExecutionContextFactory.create(setup.executionContext);
        }
        this.environments.set(envId, {
            descriptor: setup.environment,
            scopeChain: setup.scopeChain,
            manager,
            executionContext,
            connectorOverrides: setup.connectorOverrides ?? {},
        });
        this.log.info(`Environment registered: ${envId} (${setup.environment.name}, tier=${setup.environment.tier})`);
    }
    async initializeEnvironment(envId) {
        const state = this.getEnvironmentOrThrow(envId);
        if (state.executionContext) {
            await state.executionContext.initialize();
            this.log.info(`Environment "${envId}" execution context initialized`);
        }
    }
    async unregisterEnvironment(envId) {
        const state = this.environments.get(envId);
        if (!state)
            return;
        await state.manager.disconnectAll();
        if (state.executionContext) {
            await state.executionContext.teardown();
        }
        this.environments.delete(envId);
        this.log.info(`Environment unregistered: ${envId}`);
    }
    getEnvironment(envId) {
        return this.environments.get(envId)?.descriptor;
    }
    listEnvironments() {
        return Array.from(this.environments.values()).map((s) => s.descriptor);
    }
    resolveConnectorConfig(envId, connectorId) {
        const state = this.getEnvironmentOrThrow(envId);
        const resolved = this.scopedRegistry.resolve(connectorId, state.scopeChain);
        if (!resolved)
            return undefined;
        const overrides = state.connectorOverrides[connectorId];
        if (overrides) {
            if (overrides.auth) {
                resolved.auth = overrides.auth;
                resolved.config.auth = overrides.auth;
            }
            if (overrides.options) {
                resolved.options = { ...resolved.options, ...overrides.options };
                resolved.config.options = resolved.options;
            }
            if (overrides.name) {
                resolved.config.name = overrides.name;
            }
        }
        return resolved;
    }
    async createConnectorFromScope(envId, connectorId, connector) {
        const state = this.getEnvironmentOrThrow(envId);
        const resolved = this.resolveConnectorConfig(envId, connectorId);
        if (!resolved) {
            throw new Error(`No scoped config for connector "${connectorId}" in environment "${envId}". ` +
                `Scope chain: ${JSON.stringify(state.scopeChain)}`);
        }
        if (!resolved.enabled) {
            throw new Error(`Connector "${connectorId}" is disabled in environment "${envId}"`);
        }
        return state.manager.createInstance(resolved.config, connector);
    }
    async connect(envId, instanceId) {
        const state = this.getEnvironmentOrThrow(envId);
        return state.manager.connect(instanceId);
    }
    async disconnect(envId, instanceId) {
        const state = this.getEnvironmentOrThrow(envId);
        return state.manager.disconnect(instanceId);
    }
    async execute(envId, params) {
        const state = this.getEnvironmentOrThrow(envId);
        return state.manager.execute(params);
    }
    async healthCheck(envId, instanceId) {
        const state = this.getEnvironmentOrThrow(envId);
        return state.manager.healthCheck(instanceId);
    }
    listInstances(envId) {
        const state = this.getEnvironmentOrThrow(envId);
        return state.manager.listInstances();
    }
    listConnectedInstances(envId) {
        const state = this.getEnvironmentOrThrow(envId);
        return state.manager.listConnectedInstances();
    }
    getExecutionContext(envId) {
        return this.environments.get(envId)?.executionContext;
    }
    getConnectorManager(envId) {
        return this.getEnvironmentOrThrow(envId).manager;
    }
    getScopedRegistry() {
        return this.scopedRegistry;
    }
    getCredentialVault() {
        return this.credentialVault;
    }
    async teardownAll() {
        const envIds = Array.from(this.environments.keys());
        await Promise.allSettled(envIds.map((envId) => this.unregisterEnvironment(envId)));
    }
    on(listener) {
        this.listeners.push(listener);
    }
    off(listener) {
        const index = this.listeners.indexOf(listener);
        if (index >= 0)
            this.listeners.splice(index, 1);
    }
    emit(event) {
        for (const listener of this.listeners) {
            try {
                listener(event);
            }
            catch (err) {
                this.log.error('Environment event listener error', {
                    error: toErrorMessage(err),
                });
            }
        }
    }
    getEnvironmentOrThrow(envId) {
        const state = this.environments.get(envId);
        if (!state) {
            throw new Error(`Environment not found: "${envId}". Register it first via registerEnvironment().`);
        }
        return state;
    }
}
//# sourceMappingURL=environment.js.map