import { describe, it, expect, beforeEach } from 'vitest';
import {
	registerPlugin,
	unregisterPlugin,
	getPlugin,
	hasPlugin,
	getAllPlugins,
	getPluginIds,
	clearPlugins,
} from '../core/plugin-registry';
import { PluginError } from '../core/plugin-types';
import type { CanvasPlugin } from '../core/plugin-types';

function makePlugin(id: string, overrides: Partial<CanvasPlugin> = {}): CanvasPlugin {
	return {
		id,
		name: `Plugin ${id}`,
		version: '1.0.0',
		...overrides,
	};
}

describe('plugin-registry', () => {
	beforeEach(() => {
		clearPlugins();
	});

	it('starts with no plugins', () => {
		expect(getAllPlugins()).toEqual([]);
		expect(getPluginIds()).toEqual([]);
	});

	it('registerPlugin adds a plugin', () => {
		registerPlugin(makePlugin('p1'));
		expect(hasPlugin('p1')).toBe(true);
		expect(getPlugin('p1')?.id).toBe('p1');
	});

	it('getAllPlugins returns all registered', () => {
		registerPlugin(makePlugin('p1'));
		registerPlugin(makePlugin('p2'));
		expect(getAllPlugins()).toHaveLength(2);
	});

	it('unregisterPlugin removes a plugin', () => {
		registerPlugin(makePlugin('p1'));
		unregisterPlugin('p1');
		expect(hasPlugin('p1')).toBe(false);
	});

	it('throws ALREADY_REGISTERED for duplicate IDs', () => {
		registerPlugin(makePlugin('p1'));
		expect(() => registerPlugin(makePlugin('p1'))).toThrow(PluginError);
		try {
			registerPlugin(makePlugin('p1'));
		} catch (e) {
			expect((e as PluginError).code).toBe('ALREADY_REGISTERED');
		}
	});

	it('throws NOT_FOUND for unregistering unknown plugin', () => {
		expect(() => unregisterPlugin('nonexistent')).toThrow(PluginError);
	});

	it('throws MISSING_DEPENDENCY for unmet dependencies', () => {
		expect(() =>
			registerPlugin(makePlugin('p2', { dependencies: ['p1'] }))
		).toThrow(PluginError);
	});

	it('allows registration with satisfied dependencies', () => {
		registerPlugin(makePlugin('p1'));
		registerPlugin(makePlugin('p2', { dependencies: ['p1'] }));
		expect(hasPlugin('p2')).toBe(true);
	});

	it('blocks unregister when other plugins depend on it', () => {
		registerPlugin(makePlugin('p1'));
		registerPlugin(makePlugin('p2', { dependencies: ['p1'] }));
		expect(() => unregisterPlugin('p1')).toThrow(PluginError);
	});

	it('clearPlugins removes all plugins', () => {
		registerPlugin(makePlugin('p1'));
		registerPlugin(makePlugin('p2'));
		clearPlugins();
		expect(getAllPlugins()).toEqual([]);
	});

	it('onRegister lifecycle hook is called', () => {
		let called = false;
		registerPlugin(makePlugin('p1', {
			onRegister: () => { called = true; },
		}));
		expect(called).toBe(true);
	});

	it('onRegister cleanup is called on unregister', () => {
		let cleaned = false;
		registerPlugin(makePlugin('p1', {
			onRegister: () => () => { cleaned = true; },
		}));
		unregisterPlugin('p1');
		expect(cleaned).toBe(true);
	});
});
