import { describe, it, expect, beforeEach, vi } from 'vitest';
import { createStore } from 'jotai';
import type { CommandDefinition } from '../commands/types';
import { commandRegistry } from '../commands/registry';
import {
	inputModeAtom,
	commandLineVisibleAtom,
	commandLineStateAtom,
	commandFeedbackAtom,
	commandHistoryAtom,
	selectedSuggestionIndexAtom,
	isCommandActiveAtom,
	currentInputAtom,
	commandProgressAtom,
	openCommandLineAtom,
	closeCommandLineAtom,
	updateSearchQueryAtom,
	selectCommandAtom,
	provideInputAtom,
	setCommandErrorAtom,
	clearCommandErrorAtom,
} from '../commands/store';

function freshStore() {
	const store = createStore();
	store.set(commandLineStateAtom, { phase: 'idle' });
	store.set(commandLineVisibleAtom, false);
	store.set(inputModeAtom, { type: 'normal' });
	store.set(commandFeedbackAtom, null);
	store.set(commandHistoryAtom, []);
	return store;
}

function makeCommand(overrides: Partial<CommandDefinition> = {}): CommandDefinition {
	return {
		name: 'test-cmd',
		description: 'Test command',
		category: 'nodes',
		inputs: [],
		execute: vi.fn(),
		...overrides,
	};
}

describe('commands/store', () => {
	let store: ReturnType<typeof createStore>;

	beforeEach(() => {
		store = freshStore();
		commandRegistry.clear();
	});

	describe('openCommandLineAtom', () => {
		it('sets visible and searching', () => {
			store.set(openCommandLineAtom);
			expect(store.get(commandLineVisibleAtom)).toBe(true);
			expect(store.get(commandLineStateAtom).phase).toBe('searching');
		});
	});

	describe('closeCommandLineAtom', () => {
		it('resets all state', () => {
			store.set(openCommandLineAtom);
			store.set(closeCommandLineAtom);
			expect(store.get(commandLineVisibleAtom)).toBe(false);
			expect(store.get(commandLineStateAtom).phase).toBe('idle');
			expect(store.get(inputModeAtom).type).toBe('normal');
		});
	});

	describe('updateSearchQueryAtom', () => {
		it('updates state with query and resets selection', () => {
			store.set(openCommandLineAtom);
			store.set(selectedSuggestionIndexAtom, 5);
			store.set(updateSearchQueryAtom, 'test');
			const state = store.get(commandLineStateAtom);
			expect(state.phase).toBe('searching');
			expect(store.get(selectedSuggestionIndexAtom)).toBe(0);
		});
	});

	describe('selectCommandAtom', () => {
		it('command with no inputs goes to executing', () => {
			const cmd = makeCommand();
			store.set(selectCommandAtom, cmd);
			expect(store.get(commandLineStateAtom).phase).toBe('executing');
		});

		it('command with inputs goes to collecting', () => {
			const cmd = makeCommand({
				inputs: [{ name: 'pos', type: 'point', prompt: 'Pick a point' }],
			});
			store.set(selectCommandAtom, cmd);
			expect(store.get(commandLineStateAtom).phase).toBe('collecting');
			expect(store.get(inputModeAtom).type).toBe('pickPoint');
		});

		it('adds command to history', () => {
			const cmd = makeCommand({ name: 'my-cmd' });
			store.set(selectCommandAtom, cmd);
			expect(store.get(commandHistoryAtom)[0]).toBe('my-cmd');
		});
	});

	describe('provideInputAtom', () => {
		it('advances to next input', () => {
			const cmd = makeCommand({
				inputs: [
					{ name: 'a', type: 'text', prompt: 'First' },
					{ name: 'b', type: 'text', prompt: 'Second' },
				],
			});
			store.set(selectCommandAtom, cmd);
			store.set(provideInputAtom, 'val-a');
			const state = store.get(commandLineStateAtom);
			if (state.phase === 'collecting') {
				expect(state.inputIndex).toBe(1);
				expect(state.collected['a']).toBe('val-a');
			}
		});

		it('validation failure sets error', () => {
			const cmd = makeCommand({
				inputs: [{ name: 'x', type: 'text', prompt: 'Enter', validate: () => 'Bad value' }],
			});
			store.set(selectCommandAtom, cmd);
			store.set(provideInputAtom, 'anything');
			expect(store.get(commandLineStateAtom).phase).toBe('error');
		});
	});

	describe('setCommandErrorAtom / clearCommandErrorAtom', () => {
		it('sets and clears error', () => {
			store.set(setCommandErrorAtom, 'Something went wrong');
			expect(store.get(commandLineStateAtom).phase).toBe('error');
			store.set(clearCommandErrorAtom);
			expect(store.get(commandLineStateAtom).phase).toBe('idle');
		});
	});

	describe('derived atoms', () => {
		it('isCommandActiveAtom is false when idle', () => {
			expect(store.get(isCommandActiveAtom)).toBe(false);
		});

		it('isCommandActiveAtom is true when collecting', () => {
			const cmd = makeCommand({
				inputs: [{ name: 'x', type: 'text', prompt: 'Enter' }],
			});
			store.set(selectCommandAtom, cmd);
			expect(store.get(isCommandActiveAtom)).toBe(true);
		});

		it('currentInputAtom returns current input def', () => {
			const cmd = makeCommand({
				inputs: [{ name: 'x', type: 'text', prompt: 'Enter X' }],
			});
			store.set(selectCommandAtom, cmd);
			const input = store.get(currentInputAtom);
			expect(input?.name).toBe('x');
		});

		it('commandProgressAtom shows correct progress', () => {
			const cmd = makeCommand({
				inputs: [
					{ name: 'a', type: 'text', prompt: 'A' },
					{ name: 'b', type: 'text', prompt: 'B' },
				],
			});
			store.set(selectCommandAtom, cmd);
			const progress = store.get(commandProgressAtom);
			expect(progress).toEqual({ current: 1, total: 2 });
		});
	});
});
