import { describe, it, expect, vi, beforeEach } from 'vitest';
import { registerBuiltInActions } from '../core/built-in-actions';
import { getAllActions, getAction, getActionsByCategories } from '../core/action-registry';
import { BuiltInActionId, ActionCategory, type ActionContext, type ActionHelpers } from '../core/settings-types';

describe('built-in-actions', () => {
	beforeEach(() => {
		// Force re-registration since action-registry auto-registers on import
		// getAllActions() will return the already-registered actions
	});

	describe('registration', () => {
		it('registers all 18 built-in actions', () => {
			const actions = getAllActions();
			const builtInActions = actions.filter(a => a.isBuiltIn);
			expect(builtInActions.length).toBeGreaterThanOrEqual(18);
		});

		it('every BuiltInActionId has a registered action', () => {
			for (const id of Object.values(BuiltInActionId)) {
				const action = getAction(id);
				expect(action, `Action ${id} should be registered`).toBeDefined();
			}
		});

		it('actions are grouped by categories', () => {
			const categories = getActionsByCategories();
			expect(categories.length).toBeGreaterThan(0);
			// Should have Selection, Viewport, Node, Layout, History at minimum
			const categoryNames = categories.map(c => c.category);
			expect(categoryNames).toContain(ActionCategory.Selection);
			expect(categoryNames).toContain(ActionCategory.Viewport);
		});
	});

	describe('Selection actions', () => {
		it('SelectNode calls helpers.selectNode', () => {
			const action = getAction(BuiltInActionId.SelectNode)!;
			const helpers = { selectNode: vi.fn() } as any;
			const context = { nodeId: 'n1' } as ActionContext;

			action.handler(context, helpers);
			expect(helpers.selectNode).toHaveBeenCalledWith('n1');
		});

		it('AddToSelection calls helpers.addToSelection', () => {
			const action = getAction(BuiltInActionId.AddToSelection)!;
			const helpers = { addToSelection: vi.fn() } as any;
			const context = { nodeId: 'n1' } as ActionContext;

			action.handler(context, helpers);
			expect(helpers.addToSelection).toHaveBeenCalledWith('n1');
		});

		it('ClearSelection calls helpers.clearSelection', () => {
			const action = getAction(BuiltInActionId.ClearSelection)!;
			const helpers = { clearSelection: vi.fn() } as any;

			action.handler({} as ActionContext, helpers);
			expect(helpers.clearSelection).toHaveBeenCalled();
		});

		it('SelectNode skips when no nodeId', () => {
			const action = getAction(BuiltInActionId.SelectNode)!;
			const helpers = { selectNode: vi.fn() } as any;

			action.handler({} as ActionContext, helpers);
			expect(helpers.selectNode).not.toHaveBeenCalled();
		});
	});

	describe('Viewport actions', () => {
		it('ResetViewport calls helpers.resetViewport', () => {
			const action = getAction(BuiltInActionId.ResetViewport)!;
			const helpers = { resetViewport: vi.fn() } as any;

			action.handler({} as ActionContext, helpers);
			expect(helpers.resetViewport).toHaveBeenCalled();
		});

		it('FitToView calls helpers.centerOnNode', () => {
			const action = getAction(BuiltInActionId.FitToView)!;
			const helpers = { centerOnNode: vi.fn() } as any;
			const context = { nodeId: 'n1' } as ActionContext;

			action.handler(context, helpers);
			expect(helpers.centerOnNode).toHaveBeenCalledWith('n1');
		});

		it('FitAllToView calls helpers.fitToBounds', () => {
			const action = getAction(BuiltInActionId.FitAllToView)!;
			const helpers = { fitToBounds: vi.fn() } as any;

			action.handler({} as ActionContext, helpers);
			expect(helpers.fitToBounds).toHaveBeenCalledWith('graph');
		});
	});

	describe('Node actions', () => {
		it('LockNode calls helpers.lockNode', () => {
			const action = getAction(BuiltInActionId.LockNode)!;
			const helpers = { lockNode: vi.fn() } as any;

			action.handler({ nodeId: 'n1' } as ActionContext, helpers);
			expect(helpers.lockNode).toHaveBeenCalledWith('n1');
		});

		it('None action does nothing', () => {
			const action = getAction(BuiltInActionId.None)!;
			// Should not throw
			action.handler({} as ActionContext, {} as any);
		});
	});

	describe('History actions', () => {
		it('Undo calls helpers.undo when possible', () => {
			const action = getAction(BuiltInActionId.Undo)!;
			const helpers = { canUndo: vi.fn(() => true), undo: vi.fn() } as any;

			action.handler({} as ActionContext, helpers);
			expect(helpers.undo).toHaveBeenCalled();
		});

		it('Undo skips when canUndo is false', () => {
			const action = getAction(BuiltInActionId.Undo)!;
			const helpers = { canUndo: vi.fn(() => false), undo: vi.fn() } as any;

			action.handler({} as ActionContext, helpers);
			expect(helpers.undo).not.toHaveBeenCalled();
		});
	});
});
