import { toErrorMessage } from '../../utils/error.js';
import { generateConnectorInstanceId } from '../../utils/id.js';
import { getRootLogger } from '../../utils/logger.js';
export class ConnectorManager {
    registry;
    instances = new Map();
    liveConnectors = new Map();
    listeners = [];
    log;
    constructor(config) {
        this.registry = config.registry;
        this.log = getRootLogger().child({ component: 'ConnectorManager' });
    }
    on(listener) {
        this.listeners.push(listener);
    }
    off(listener) {
        const index = this.listeners.indexOf(listener);
        if (index >= 0)
            this.listeners.splice(index, 1);
    }
    async createInstance(config, connector) {
        const definition = this.registry.getOrThrow(config.connectorId);
        const parsedConfig = definition.configSchema.safeParse(config.options ?? {});
        if (!parsedConfig.success) {
            const errors = parsedConfig.error.issues
                .map((i) => `${i.path.join('.')}: ${i.message}`)
                .join('; ');
            throw new Error(`Invalid config for connector "${config.connectorId}": ${errors}`);
        }
        const instanceId = generateConnectorInstanceId();
        const instance = {
            id: instanceId,
            connectorId: config.connectorId,
            config,
            status: 'disconnected',
            createdAt: Date.now(),
        };
        this.instances.set(instanceId, instance);
        this.liveConnectors.set(instanceId, connector);
        this.emit({ type: 'instance_created', instanceId, connectorId: config.connectorId });
        this.log.info(`Connector instance created: ${instanceId} (${config.connectorId})`);
        return instance;
    }
    async connect(instanceId) {
        const instance = this.getInstanceOrThrow(instanceId);
        const connector = this.getConnectorOrThrow(instanceId);
        this.updateStatus(instanceId, 'connecting');
        this.emit({ type: 'instance_connecting', instanceId });
        try {
            const definition = this.registry.getOrThrow(instance.connectorId);
            const parsedConfig = definition.configSchema.parse(instance.config.options ?? {});
            await connector.connect(parsedConfig, instance.config.auth);
            this.updateStatus(instanceId, 'connected');
            instance.connectedAt = Date.now();
            this.emit({ type: 'instance_connected', instanceId });
            this.log.info(`Connector connected: ${instanceId}`);
        }
        catch (err) {
            const message = toErrorMessage(err);
            this.updateStatus(instanceId, 'error', message);
            this.emit({ type: 'instance_error', instanceId, error: message });
            this.log.error(`Connector connection failed: ${instanceId}`, { error: message });
            throw err;
        }
    }
    async disconnect(instanceId) {
        const connector = this.getConnectorOrThrow(instanceId);
        try {
            await connector.disconnect();
            this.updateStatus(instanceId, 'disconnected');
            this.emit({ type: 'instance_disconnected', instanceId });
            this.log.info(`Connector disconnected: ${instanceId}`);
        }
        catch (err) {
            const message = toErrorMessage(err);
            this.log.error(`Connector disconnect failed: ${instanceId}`, { error: message });
            throw err;
        }
    }
    async healthCheck(instanceId) {
        const connector = this.getConnectorOrThrow(instanceId);
        try {
            return await connector.healthCheck();
        }
        catch {
            return false;
        }
    }
    async execute(params) {
        const instance = this.getInstanceOrThrow(params.instanceId);
        const connector = this.getConnectorOrThrow(params.instanceId);
        if (instance.status !== 'connected') {
            return {
                success: false,
                output: null,
                error: `Connector "${params.instanceId}" is not connected (status: ${instance.status})`,
                durationMs: 0,
            };
        }
        this.emit({ type: 'action_executing', instanceId: params.instanceId, method: params.method });
        const start = performance.now();
        try {
            const result = await connector.execute(params.method, params.input);
            instance.lastUsedAt = Date.now();
            this.emit({
                type: 'action_completed',
                instanceId: params.instanceId,
                method: params.method,
                success: result.success,
                durationMs: result.durationMs,
            });
            return result;
        }
        catch (err) {
            const message = toErrorMessage(err);
            const durationMs = Math.round(performance.now() - start);
            this.emit({
                type: 'action_completed',
                instanceId: params.instanceId,
                method: params.method,
                success: false,
                durationMs,
            });
            return {
                success: false,
                output: null,
                error: `Execution failed: ${message}`,
                durationMs,
            };
        }
    }
    async removeInstance(instanceId) {
        const instance = this.instances.get(instanceId);
        if (!instance)
            return;
        if (instance.status === 'connected') {
            await this.disconnect(instanceId).catch((err) => {
                this.log.warn('Disconnect failed during removal', {
                    instanceId,
                    error: toErrorMessage(err),
                });
            });
        }
        this.instances.delete(instanceId);
        this.liveConnectors.delete(instanceId);
        this.emit({ type: 'instance_removed', instanceId });
        this.log.info(`Connector instance removed: ${instanceId}`);
    }
    getRegistry() {
        return this.registry;
    }
    getInstance(instanceId) {
        return this.instances.get(instanceId);
    }
    getConnector(instanceId) {
        return this.liveConnectors.get(instanceId);
    }
    listInstances() {
        return Array.from(this.instances.values());
    }
    listInstancesByConnector(connectorId) {
        return this.listInstances().filter((i) => i.connectorId === connectorId);
    }
    listConnectedInstances() {
        return this.listInstances().filter((i) => i.status === 'connected');
    }
    async disconnectAll() {
        const connected = this.listInstances().filter((i) => i.status === 'connected');
        await Promise.allSettled(connected.map((i) => this.disconnect(i.id)));
    }
    getInstanceOrThrow(instanceId) {
        const instance = this.instances.get(instanceId);
        if (!instance) {
            throw new Error(`Connector instance not found: "${instanceId}"`);
        }
        return instance;
    }
    getConnectorOrThrow(instanceId) {
        const connector = this.liveConnectors.get(instanceId);
        if (!connector) {
            throw new Error(`Live connector not found for instance: "${instanceId}"`);
        }
        return connector;
    }
    updateStatus(instanceId, status, error) {
        const instance = this.instances.get(instanceId);
        if (instance) {
            instance.status = status;
            instance.error = error;
        }
    }
    emit(event) {
        for (const listener of this.listeners) {
            try {
                listener(event);
            }
            catch (err) {
                this.log.error('Connector event listener error', {
                    error: toErrorMessage(err),
                });
            }
        }
    }
}
//# sourceMappingURL=lifecycle.js.map