import { useCallback, useMemo } from 'react';

import { getUserAgent } from '../utils/get-user-agent';

type Action = 'selectAll' | 'escape' | 'clear' | 'enter' | 'paste';
type ActionHandlers = {
  [key in `on${Capitalize<Action>}`]?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
};
type KeyboardCommand = {
  match: (event: React.KeyboardEvent<HTMLInputElement>) => boolean;
  execute: (event: React.KeyboardEvent<HTMLInputElement>) => void;
};

export function useInputKeyCommands(actionHandlers?: ActionHandlers) {
  // MacOS uses Command key instead of Ctrl
  const ctrlKey = useMemo(() => (getUserAgent().includes('Mac') ? 'metaKey' : 'ctrlKey'), []);

  const isSelectAll = useCallback(
    (event: React.KeyboardEvent<HTMLInputElement>) => {
      return event.key === 'a' && event[ctrlKey];
    },
    [ctrlKey],
  );

  const isPaste = useCallback(
    (event: React.KeyboardEvent<HTMLInputElement>) => {
      return event.key === 'v' && event[ctrlKey];
    },
    [ctrlKey],
  );

  const commands = useMemo<KeyboardCommand[]>(
    () => [
      {
        match: (event) => event.key === 'Enter',
        execute: (event) => actionHandlers?.onEnter?.(event),
      },
      {
        match: (event) => event.key === 'Escape',
        execute: (event) => {
          actionHandlers?.onEscape?.(event);

          if (event.currentTarget?.selectionStart !== event.currentTarget?.selectionEnd) {
            event.stopPropagation();
            removeSelection(event);
          }
        },
      },
      {
        match: isSelectAll,
        execute: (event) => actionHandlers?.onSelectAll?.(event),
      },
      {
        match: isPaste,
        execute: (event) => actionHandlers?.onPaste?.(event),
      },
      {
        match: (event) => {
          if (!event.currentTarget?.value) return false;

          const selectionLength =
            (event.currentTarget?.selectionEnd ?? 0) - (event.currentTarget?.selectionStart ?? 0);
          const isFullValueSelected = event.currentTarget?.value.length === selectionLength;
          const isModifyAction = isPrintableCharacter(event) || isPaste(event) || isDelete(event);

          return isFullValueSelected && isModifyAction;
        },

        execute: (event) => actionHandlers?.onClear?.(event),
      },
    ],
    [actionHandlers, isPaste, isSelectAll],
  );

  const onKeyDown = useCallback(
    (event: React.KeyboardEvent<HTMLInputElement>) => {
      for (const command of commands) {
        if (command.match(event)) {
          command.execute(event);
        }
      }
    },
    [commands],
  );

  return { onKeyDown };
}

function removeSelection(event: React.KeyboardEvent<HTMLInputElement>) {
  const selectionEnd = event.currentTarget.selectionEnd ?? 0;
  event.currentTarget.setSelectionRange(selectionEnd, selectionEnd);
}

function isPrintableCharacter(event: React.KeyboardEvent<HTMLInputElement>) {
  return event.key.length === 1 && !event.ctrlKey && !event.metaKey;
}

function isDelete(event: React.KeyboardEvent<HTMLInputElement>) {
  return event.key === 'Backspace' || event.key === 'Delete';
}
