import React from "react";
import { Popover } from "./Popover";

import "./IconPicker.scss";
import { isArrowKey, KEYS } from "../keys";
import { getLanguage } from "../i18n";
import clsx from "clsx";

function Picker<T>({
  options,
  value,
  label,
  onChange,
  onClose,
}: {
  label: string;
  value: T;
  options: {
    value: T;
    text: string;
    icon: JSX.Element;
    keyBinding: string | null;
  }[];
  onChange: (value: T) => void;
  onClose: () => void;
}) {
  const rFirstItem = React.useRef<HTMLButtonElement>();
  const rActiveItem = React.useRef<HTMLButtonElement>();
  const rGallery = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    // After the component is first mounted focus on first input
    if (rActiveItem.current) {
      rActiveItem.current.focus();
    } else if (rGallery.current) {
      rGallery.current.focus();
    }
  }, []);

  const handleKeyDown = (event: React.KeyboardEvent) => {
    const pressedOption = options.find(
      (option) => option.keyBinding === event.key.toLowerCase(),
    )!;

    if (!(event.metaKey || event.altKey || event.ctrlKey) && pressedOption) {
      // Keybinding navigation
      const index = options.indexOf(pressedOption);
      (rGallery!.current!.children![index] as any).focus();
      event.preventDefault();
    } else if (event.key === KEYS.TAB) {
      // Tab navigation cycle through options. If the user tabs
      // away from the picker, close the picker. We need to use
      // a timeout here to let the stack clear before checking.
      setTimeout(() => {
        const active = rActiveItem.current;
        const docActive = document.activeElement;
        if (active !== docActive) {
          onClose();
        }
      }, 0);
    } else if (isArrowKey(event.key)) {
      // Arrow navigation
      const { activeElement } = document;
      const isRTL = getLanguage().rtl;
      const index = Array.prototype.indexOf.call(
        rGallery!.current!.children,
        activeElement,
      );
      if (index !== -1) {
        const length = options.length;
        let nextIndex = index;

        switch (event.key) {
          // Select the next option
          case isRTL ? KEYS.ARROW_LEFT : KEYS.ARROW_RIGHT:
          case KEYS.ARROW_DOWN: {
            nextIndex = (index + 1) % length;
            break;
          }
          // Select the previous option
          case isRTL ? KEYS.ARROW_RIGHT : KEYS.ARROW_LEFT:
          case KEYS.ARROW_UP: {
            nextIndex = (length + index - 1) % length;
            break;
          }
        }

        (rGallery.current!.children![nextIndex] as any).focus();
      }
      event.preventDefault();
    } else if (event.key === KEYS.ESCAPE || event.key === KEYS.ENTER) {
      // Close on escape or enter
      event.preventDefault();
      onClose();
    }
    event.nativeEvent.stopImmediatePropagation();
    event.stopPropagation();
  };

  return (
    <div
      className={`picker`}
      role="dialog"
      aria-modal="true"
      aria-label={label}
      onKeyDown={handleKeyDown}
    >
      <div className="picker-content" ref={rGallery}>
        {options.map((option, i) => (
          <button
            type="button"
            className={clsx("picker-option", {
              active: value === option.value,
            })}
            onClick={(event) => {
              (event.currentTarget as HTMLButtonElement).focus();
              onChange(option.value);
            }}
            title={`${option.text} ${
              option.keyBinding && `— ${option.keyBinding.toUpperCase()}`
            }`}
            aria-label={option.text || "none"}
            aria-keyshortcuts={option.keyBinding || undefined}
            key={option.text}
            ref={(el) => {
              if (el && i === 0) {
                rFirstItem.current = el;
              }
              if (el && option.value === value) {
                rActiveItem.current = el;
              }
            }}
            onFocus={() => {
              onChange(option.value);
            }}
          >
            {option.icon}
            {option.keyBinding && (
              <span className="picker-keybinding">{option.keyBinding}</span>
            )}
          </button>
        ))}
      </div>
    </div>
  );
}

export function IconPicker<T>({
  value,
  label,
  options,
  onChange,
  group = "",
}: {
  label: string;
  value: T;
  options: readonly {
    value: T;
    text: string;
    icon: JSX.Element;
    keyBinding: string | null;
    showInPicker?: boolean;
  }[];
  onChange: (value: T) => void;
  group?: string;
}) {
  const [isActive, setActive] = React.useState(false);
  const rPickerButton = React.useRef<any>(null);
  const isRTL = getLanguage().rtl;

  return (
    <div>
      <button
        name={group}
        type="button"
        className={isActive ? "active" : ""}
        aria-label={label}
        onClick={() => setActive(!isActive)}
        ref={rPickerButton}
      >
        {options.find((option) => option.value === value)?.icon}
      </button>
      <React.Suspense fallback="">
        {isActive ? (
          <>
            <Popover
              onCloseRequest={(event) =>
                event.target !== rPickerButton.current && setActive(false)
              }
              {...(isRTL ? { right: 5.5 } : { left: -5.5 })}
            >
              <Picker
                options={options.filter((opt) => opt.showInPicker !== false)}
                value={value}
                label={label}
                onChange={onChange}
                onClose={() => {
                  setActive(false);
                  rPickerButton.current?.focus();
                }}
              />
            </Popover>
            <div className="picker-triangle" />
          </>
        ) : null}
      </React.Suspense>
    </div>
  );
}