import CommandService from '../CommandService';
import { stateUtils } from '../../reducer';
import focusEditorIfEditorCommand from './focusEditorIfEditorCommand';

const separatorItem = { type: 'separator' };

export interface ToolbarButtonInfo {
	name: string;
	tooltip: string;
	iconName: string;
	enabled: boolean;
	onClick(): void;
	title: string;
}

interface ToolbarButtonCacheItem {
	info: ToolbarButtonInfo;
}

interface ToolbarButtonCache {
	[key: string]: ToolbarButtonCacheItem;
}

export default class ToolbarButtonUtils {

	private service_: CommandService;
	private toolbarButtonCache_: ToolbarButtonCache = {};

	public constructor(service: CommandService) {
		this.service_ = service;
	}

	private get service(): CommandService {
		return this.service_;
	}

	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	private commandToToolbarButton(commandName: string, whenClauseContext: any): ToolbarButtonInfo {
		const newEnabled = this.service.isEnabled(commandName, whenClauseContext);
		const newTitle = this.service.title(commandName);

		if (
			this.toolbarButtonCache_[commandName] &&
			this.toolbarButtonCache_[commandName].info.enabled === newEnabled &&
			this.toolbarButtonCache_[commandName].info.title === newTitle
		) {
			return this.toolbarButtonCache_[commandName].info;
		}

		const command = this.service.commandByName(commandName, { runtimeMustBeRegistered: true });

		const output = {
			name: commandName,
			tooltip: this.service.label(commandName),
			iconName: command.declaration.iconName,
			enabled: newEnabled,
			onClick: async () => {
				void this.service.execute(commandName);
				void focusEditorIfEditorCommand(commandName, this.service);
			},
			title: newTitle,
		};

		this.toolbarButtonCache_[commandName] = {
			info: output,
		};

		return this.toolbarButtonCache_[commandName].info;
	}

	// This method ensures that if the provided commandNames and state hasn't changed
	// the output also won't change. Invididual toolbarButtonInfo also won't changed
	// if the state they use hasn't changed. This is to avoid useless renders of the toolbars.
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	public commandsToToolbarButtons(commandNames: string[], whenClauseContext: any): ToolbarButtonInfo[] {
		const output: ToolbarButtonInfo[] = [];

		for (const commandName of commandNames) {
			if (commandName === '-') {
				// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
				output.push(separatorItem as any);
				continue;
			}

			output.push(this.commandToToolbarButton(commandName, whenClauseContext));
		}

		return stateUtils.selectArrayShallow({ array: output }, commandNames.join('_'));
	}

}
