import type { BaseService } from '@learnitive/base-utils';
import type { InferType, Validator } from '@learnitive/mini-js-utils';
import type { CommandExposedServiceSlotId, Store } from './services';

// To keep things simple we are only allowing a select few types
export type AllowedValidator =
  | Validator<string>
  | Validator<number>
  | Validator<boolean>
  | Validator<string[]>
  | Validator<string | boolean | number | undefined>;

export type OmniSearchScope = 'global' | 'workspace' | 'note';

export type Command = {
  id: string;
  title?: string;
  description?: string;
  icon?: string;
  keywords?: string[];
  keybindings?: string[];
  allowShortcutInInputs?: boolean;
  dependencies?: {
    /**
     * The services that are required to be available in the command handler ctx
     */
    services?: CommandExposedServiceSlotId[];
    /**
     * The commands that the handler is allowed to dispatch
     */
    commands?: string[];
  };
  /** The narrowest app state where omni search should expose this command. */
  omniSearch?: OmniSearchScope | false;
  // If false, the editor will not be focused after the command is dispatched.
  // Defaults to true when omniSearch is enabled.
  autoFocusEditor?: boolean;
  //   the args type info when dispatching this command
  args: {
    [key: string]: AllowedValidator;
  } | null;
};

export type CommandHandlerContext = {
  store: Store;
  dispatch: (commandId: string, args: unknown) => void;
};
export type CommandHandler = (
  services: Record<string, BaseService>,
  args: unknown,
  key: CommandKey<string>,
) => void | Promise<void>;

export type CommandKey<T extends string> = { key: T };

export type CommandDispatchResult = {
  type: 'success' | 'failure';
  command: Command;
  from: string;
};

export type CommandArgs<C extends Command> = C['args'] extends null
  ? null
  : {
      [K in keyof C['args']]: C['args'][K] extends Validator<any>
        ? InferType<C['args'][K]>
        : never;
    };
