import { describe, it, expect, beforeEach, vi } from 'vitest';
import { PluginRegistry } from '../registry.js';
import { BeforeInvocationEvent } from '../../hooks/events.js';
import { createMockAgent } from '../../__fixtures__/agent-helpers.js';
import { createRandomTool } from '../../__fixtures__/tool-helpers.js';
/**
 * Test plugin implementation.
 */
class TestPlugin {
    hookRegistered = false;
    _name;
    constructor(name = 'test-plugin') {
        this._name = name;
    }
    get name() {
        return this._name;
    }
    initAgent(agent) {
        agent.addHook(BeforeInvocationEvent, () => {
            this.hookRegistered = true;
        });
    }
}
/**
 * Plugin with initAgent for testing initialization.
 */
class InitializableTestPlugin {
    _name;
    initialized = false;
    constructor(_name = 'initializable-plugin') {
        this._name = _name;
    }
    get name() {
        return this._name;
    }
    initAgent(_agent) {
        this.initialized = true;
    }
}
/**
 * Plugin that provides tools.
 */
class ToolProviderPlugin {
    _name;
    _tools;
    constructor(_name, _tools) {
        this._name = _name;
        this._tools = _tools;
    }
    get name() {
        return this._name;
    }
    initAgent(_agent) { }
    getTools() {
        return this._tools;
    }
}
describe('PluginRegistry', () => {
    let registry;
    let mockAgent;
    let registeredHooks;
    beforeEach(() => {
        registeredHooks = [];
        mockAgent = createMockAgent({
            extra: {
                addHook: (eventType, callback) => {
                    registeredHooks.push({
                        eventType: eventType,
                        callback: callback,
                    });
                    return () => { };
                },
            },
        });
    });
    describe('initialize', () => {
        it('initializes a plugin and calls initAgent', async () => {
            const plugin = new InitializableTestPlugin();
            registry = new PluginRegistry([plugin]);
            await registry.initialize(mockAgent);
            expect(plugin.initialized).toBe(true);
        });
        it('registers hooks via agent.addHook', async () => {
            const plugin = new TestPlugin();
            registry = new PluginRegistry([plugin]);
            await registry.initialize(mockAgent);
            expect(registeredHooks).toHaveLength(1);
            expect(registeredHooks[0]?.eventType).toBe(BeforeInvocationEvent);
        });
        it('throws error when plugins have duplicate names', async () => {
            const plugin1 = new TestPlugin('duplicate-name');
            const plugin2 = new TestPlugin('duplicate-name');
            registry = new PluginRegistry([plugin1, plugin2]);
            await expect(registry.initialize(mockAgent)).rejects.toThrow('plugin_name=<duplicate-name> | plugin already registered');
        });
        it('initializes multiple plugins with different names', async () => {
            const plugin1 = new TestPlugin('plugin-1');
            const plugin2 = new TestPlugin('plugin-2');
            registry = new PluginRegistry([plugin1, plugin2]);
            await registry.initialize(mockAgent);
            expect(registeredHooks).toHaveLength(2);
        });
        it('auto-registers tools from plugin.getTools()', async () => {
            const mockTool = createRandomTool('mock-tool');
            const plugin = new ToolProviderPlugin('tool-provider', [mockTool]);
            registry = new PluginRegistry([plugin]);
            await registry.initialize(mockAgent);
            expect(mockAgent.toolRegistry.get(mockTool.name)).toBe(mockTool);
        });
        it('handles async initAgent', async () => {
            class AsyncPlugin {
                initialized = false;
                get name() {
                    return 'async-plugin';
                }
                async initAgent(_agent) {
                    await vi.waitFor(() => Promise.resolve());
                    this.initialized = true;
                }
            }
            const plugin = new AsyncPlugin();
            registry = new PluginRegistry([plugin]);
            await registry.initialize(mockAgent);
            expect(plugin.initialized).toBe(true);
        });
        it('is idempotent — calling initialize twice only runs plugins once', async () => {
            const plugin = new InitializableTestPlugin();
            registry = new PluginRegistry([plugin]);
            await registry.initialize(mockAgent);
            plugin.initialized = false; // reset to detect a second call
            await registry.initialize(mockAgent);
            expect(plugin.initialized).toBe(false);
        });
    });
    describe('hook invocation', () => {
        it('hooks are invoked when callbacks are called', async () => {
            const plugin = new TestPlugin();
            registry = new PluginRegistry([plugin]);
            await registry.initialize(mockAgent);
            const callback = registeredHooks[0]?.callback;
            const mockAgentData = {};
            callback?.(new BeforeInvocationEvent({ agent: mockAgentData, invocationState: {} }));
            expect(plugin.hookRegistered).toBe(true);
        });
    });
});
//# sourceMappingURL=registry.test.js.map