/**
 * AdapterManager: central registry for provider adapters.
 * Handles adapter lifecycle (discovery, initialization, health, disposal)
 * and tracks the active adapter for the current session.
 *
 * @task T5240
 */
import { hooks } from '../hooks/registry.js';
import { getLogger } from '../logger.js';
import { loadAdapterFromManifest } from './adapter-registry.js';
import { detectProvider, discoverAdapterManifests } from './discovery.js';
const log = getLogger('adapter-manager');
/**
 * Central adapter manager. Singleton per process.
 *
 * Lifecycle:
 *   1. discover() — scan for adapter packages and their manifests
 *   2. activate(id) — load, initialize, and set as active adapter
 *   3. getActive() — return the current active adapter
 *   4. dispose() — clean up all initialized adapters
 */
export class AdapterManager {
    static instance = null;
    adapters = new Map();
    manifests = new Map();
    hookCleanups = new Map();
    activeId = null;
    projectRoot;
    constructor(projectRoot) {
        this.projectRoot = projectRoot;
    }
    static getInstance(projectRoot) {
        if (!AdapterManager.instance) {
            AdapterManager.instance = new AdapterManager(projectRoot);
        }
        return AdapterManager.instance;
    }
    /** Reset singleton (for testing). */
    static resetInstance() {
        AdapterManager.instance = null;
    }
    /**
     * Discover adapter manifests from packages/adapters/.
     * Returns manifests found (does not load adapter code yet).
     */
    discover() {
        const found = discoverAdapterManifests(this.projectRoot);
        for (const manifest of found) {
            this.manifests.set(manifest.id, manifest);
        }
        log.info({ count: found.length }, 'Discovered adapter manifests');
        return found;
    }
    /**
     * Auto-detect which adapters match the current environment
     * and return their manifest IDs.
     */
    detectActive() {
        const detected = [];
        for (const [id, manifest] of this.manifests) {
            if (detectProvider(manifest.detectionPatterns)) {
                detected.push(id);
            }
        }
        log.info({ detected }, 'Detected active providers');
        return detected;
    }
    /**
     * Load and initialize an adapter by manifest ID.
     * Dynamically imports from the manifest's packagePath — no hardcoded adapters.
     */
    async activate(adapterId) {
        const manifest = this.manifests.get(adapterId);
        if (!manifest) {
            throw new Error(`Adapter manifest not found: ${adapterId}`);
        }
        // Check if already loaded
        const existing = this.adapters.get(adapterId);
        if (existing) {
            this.activeId = adapterId;
            return existing;
        }
        try {
            // Dynamic import from discovered package path — no hardcoded adapters
            const adapter = await loadAdapterFromManifest(manifest);
            await adapter.initialize(this.projectRoot);
            this.adapters.set(adapterId, adapter);
            this.activeId = adapterId;
            // Wire hooks into HookRegistry
            if (adapter.hooks) {
                await this.wireAdapterHooks(adapterId, adapter);
            }
            log.info({ adapterId, provider: manifest.provider }, 'Adapter activated');
            return adapter;
        }
        catch (err) {
            log.error({ adapterId, err }, 'Failed to activate adapter');
            throw err;
        }
    }
    /** Get the currently active adapter, or null if none. */
    getActive() {
        if (!this.activeId)
            return null;
        return this.adapters.get(this.activeId) ?? null;
    }
    /** Get the active adapter's ID, or null. */
    getActiveId() {
        return this.activeId;
    }
    /** Get a specific adapter by ID. */
    get(adapterId) {
        return this.adapters.get(adapterId) ?? null;
    }
    /** Get the manifest for a specific adapter. */
    getManifest(adapterId) {
        return this.manifests.get(adapterId) ?? null;
    }
    /** List all known adapters with summary info. */
    listAdapters() {
        const result = [];
        for (const [id, manifest] of this.manifests) {
            const adapter = this.adapters.get(id);
            result.push({
                id,
                name: manifest.name,
                version: manifest.version,
                provider: manifest.provider,
                healthy: !!adapter,
                active: id === this.activeId,
            });
        }
        return result;
    }
    /** Run health check on all initialized adapters. */
    async healthCheckAll() {
        const results = new Map();
        for (const [id, adapter] of this.adapters) {
            try {
                const status = await adapter.healthCheck();
                results.set(id, status);
            }
            catch (err) {
                results.set(id, {
                    healthy: false,
                    provider: this.manifests.get(id)?.provider ?? 'unknown',
                    details: { error: err instanceof Error ? err.message : String(err) },
                });
            }
        }
        return results;
    }
    /** Health check a single adapter. */
    async healthCheck(adapterId) {
        const adapter = this.adapters.get(adapterId);
        if (!adapter) {
            return {
                healthy: false,
                provider: this.manifests.get(adapterId)?.provider ?? 'unknown',
                details: { error: 'Adapter not initialized' },
            };
        }
        try {
            return await adapter.healthCheck();
        }
        catch (err) {
            return {
                healthy: false,
                provider: this.manifests.get(adapterId)?.provider ?? 'unknown',
                details: { error: err instanceof Error ? err.message : String(err) },
            };
        }
    }
    /** Dispose all initialized adapters. */
    async dispose() {
        for (const [id, adapter] of this.adapters) {
            try {
                // Clean up hooks first
                await this.cleanupAdapterHooks(id, adapter);
                await adapter.dispose();
                log.info({ adapterId: id }, 'Adapter disposed');
            }
            catch (err) {
                log.error({ adapterId: id, err }, 'Failed to dispose adapter');
            }
        }
        this.adapters.clear();
        this.hookCleanups.clear();
        this.activeId = null;
    }
    /** Dispose a single adapter. */
    async disposeAdapter(adapterId) {
        const adapter = this.adapters.get(adapterId);
        if (!adapter)
            return;
        try {
            await this.cleanupAdapterHooks(adapterId, adapter);
            await adapter.dispose();
        }
        catch (err) {
            log.error({ adapterId, err }, 'Failed to dispose adapter');
        }
        this.adapters.delete(adapterId);
        if (this.activeId === adapterId) {
            this.activeId = null;
        }
    }
    /**
     * Wire an adapter's hook event map into CLEO's HookRegistry.
     * Creates bridging handlers at priority 50 for each mapped event.
     */
    async wireAdapterHooks(adapterId, adapter) {
        if (!adapter.hooks)
            return;
        try {
            await adapter.hooks.registerNativeHooks(this.projectRoot);
        }
        catch (err) {
            log.error({ adapterId, err }, 'Failed to register native hooks');
        }
        const eventMap = adapter.hooks.getEventMap?.();
        if (!eventMap)
            return;
        const cleanups = [];
        for (const [_providerEvent, caampEvent] of Object.entries(eventMap)) {
            const hookId = `adapter-${adapterId}-${caampEvent}`;
            const unregister = hooks.register({
                id: hookId,
                event: caampEvent,
                priority: 50,
                handler: async (_projectRoot, payload) => {
                    log.debug({ adapterId, event: caampEvent, payload }, 'Adapter hook dispatched');
                },
            });
            cleanups.push(unregister);
        }
        this.hookCleanups.set(adapterId, cleanups);
    }
    /**
     * Clean up hook registrations for an adapter.
     */
    async cleanupAdapterHooks(adapterId, adapter) {
        // Unregister from HookRegistry
        const cleanups = this.hookCleanups.get(adapterId);
        if (cleanups) {
            for (const fn of cleanups) {
                fn();
            }
            this.hookCleanups.delete(adapterId);
        }
        // Unregister native hooks
        try {
            await adapter.hooks?.unregisterNativeHooks();
        }
        catch (err) {
            log.error({ adapterId, err }, 'Failed to unregister native hooks');
        }
    }
}
//# sourceMappingURL=manager.js.map