import { describe, it, expect, beforeEach } from 'vitest';
import { commandRegistry } from '../commands/registry';
import type { CommandDefinition } from '../commands/types';

function makeCommand(name: string, overrides: Partial<CommandDefinition> = {}): CommandDefinition {
	return {
		name,
		description: `Description for ${name}`,
		category: 'nodes',
		inputs: [],
		execute: async () => {},
		...overrides,
	};
}

describe('commands/registry', () => {
	beforeEach(() => {
		commandRegistry.clear();
	});

	describe('register / get', () => {
		it('registers and retrieves a command by name', () => {
			commandRegistry.register(makeCommand('test-cmd'));
			expect(commandRegistry.get('test-cmd')).toBeDefined();
			expect(commandRegistry.get('test-cmd')!.name).toBe('test-cmd');
		});

		it('retrieves a command by alias', () => {
			commandRegistry.register(makeCommand('test-cmd', { aliases: ['tc'] }));
			expect(commandRegistry.get('tc')).toBeDefined();
			expect(commandRegistry.get('tc')!.name).toBe('test-cmd');
		});

		it('throws on duplicate name', () => {
			commandRegistry.register(makeCommand('test-cmd'));
			expect(() => commandRegistry.register(makeCommand('test-cmd'))).toThrow();
		});

		it('throws on duplicate alias', () => {
			commandRegistry.register(makeCommand('cmd1', { aliases: ['x'] }));
			expect(() => commandRegistry.register(makeCommand('cmd2', { aliases: ['x'] }))).toThrow();
		});
	});

	describe('unregister', () => {
		it('removes command and its aliases', () => {
			commandRegistry.register(makeCommand('to-remove', { aliases: ['rm'] }));
			commandRegistry.unregister('to-remove');
			expect(commandRegistry.has('to-remove')).toBe(false);
			expect(commandRegistry.has('rm')).toBe(false);
		});
	});

	describe('has', () => {
		it('returns true for registered name or alias', () => {
			commandRegistry.register(makeCommand('exists', { aliases: ['e'] }));
			expect(commandRegistry.has('exists')).toBe(true);
			expect(commandRegistry.has('e')).toBe(true);
			expect(commandRegistry.has('nope')).toBe(false);
		});
	});

	describe('search', () => {
		beforeEach(() => {
			commandRegistry.register(makeCommand('createNode', { aliases: ['cn'], description: 'Create a new node' }));
			commandRegistry.register(makeCommand('deleteNode', { aliases: ['dn'], description: 'Delete selected node' }));
			commandRegistry.register(makeCommand('layoutGrid', { description: 'Apply grid layout' }));
		});

		it('returns all for empty query', () => {
			expect(commandRegistry.search('').length).toBe(3);
		});

		it('finds by name prefix', () => {
			const results = commandRegistry.search('create');
			expect(results[0].name).toBe('createNode');
		});

		it('finds by alias', () => {
			const results = commandRegistry.search('cn');
			expect(results[0].name).toBe('createNode');
		});

		it('finds by description', () => {
			const results = commandRegistry.search('grid');
			expect(results[0].name).toBe('layoutGrid');
		});
	});

	describe('all / byCategory / categories', () => {
		it('returns alphabetically sorted commands', () => {
			commandRegistry.register(makeCommand('zeta'));
			commandRegistry.register(makeCommand('alpha'));
			const all = commandRegistry.all();
			expect(all[0].name).toBe('alpha');
			expect(all[1].name).toBe('zeta');
		});

		it('filters by category', () => {
			commandRegistry.register(makeCommand('a', { category: 'nodes' }));
			commandRegistry.register(makeCommand('b', { category: 'viewport' }));
			expect(commandRegistry.byCategory('nodes').length).toBe(1);
		});

		it('returns unique categories', () => {
			commandRegistry.register(makeCommand('a', { category: 'nodes' }));
			commandRegistry.register(makeCommand('b', { category: 'viewport' }));
			commandRegistry.register(makeCommand('c', { category: 'nodes' }));
			expect(commandRegistry.categories().length).toBe(2);
		});
	});

	describe('size / clear', () => {
		it('tracks size', () => {
			expect(commandRegistry.size).toBe(0);
			commandRegistry.register(makeCommand('x'));
			expect(commandRegistry.size).toBe(1);
		});

		it('clear resets to empty', () => {
			commandRegistry.register(makeCommand('x', { aliases: ['y'] }));
			commandRegistry.clear();
			expect(commandRegistry.size).toBe(0);
			expect(commandRegistry.has('y')).toBe(false);
		});
	});
});
