import * as React from 'react';
import * as TextControlBase from '@tesseract-design/web-base-textcontrol';
import * as BadgeBase from '@tesseract-design/web-base-badge';

export interface TagInputProps extends Omit<React.HTMLProps<HTMLInputElement>, 'size' | 'style' | 'label'> {
  /**
   * Short textual description indicating the nature of the component's value.
   */
  label?: React.ReactNode,
  /**
   * Short textual description as guidelines for valid input values.
   */
  hint?: React.ReactNode,
  /**
   * Size of the component.
   */
  size?: TextControlBase.TextControlSize,
  /**
   * Additional description, usually graphical, indicating the nature of the component's value.
   */
  indicator?: React.ReactNode,
  /**
   * Should the component display a border?
   */
  border?: boolean,
  /**
   * Should the component occupy the whole width of its parent?
   */
  block?: boolean,
  /**
   * Style of the component.
   */
  style?: TextControlBase.TextControlStyle,
  /**
   * Is the label hidden?
   */
  hiddenLabel?: boolean,
  enhanced?: boolean,
  separator?: string,
}

export const TagInput = React.forwardRef<HTMLInputElement, TagInputProps>((
  {
    label = '',
    hint = '',
    indicator = null,
    size = TextControlBase.TextControlSize.MEDIUM,
    border = false,
    block = false,
    style = TextControlBase.TextControlStyle.DEFAULT,
    hiddenLabel = false,
    onInput,
    enhanced = false,
    defaultValue = '',
    separator = ',',
    onFocus,
    onBlur,
    onSelect,
    className: _className,
    placeholder: _placeholder,
    as: _as,
    ...etcProps
  }: TagInputProps,
  forwardedRef,
) => {
  const [hydrated, setHydrated] = React.useState(false);
  const [focused, setFocused] = React.useState(false);
  const [selectionStart, setSelectionStart] = React.useState(0);
  const [selectionEnd, setSelectionEnd] = React.useState(0);
  const [viewValue, setViewValue] = React.useState<string[]>(() => {
    const theDefaultValue = !Array.isArray(defaultValue) ? [defaultValue.toString(), ''] : [...defaultValue, ''];
    return theDefaultValue.filter(v => v.length > 0);
  });
  const defaultRef = React.useRef<HTMLInputElement>(null);
  const effectiveRef = forwardedRef ?? defaultRef;

  const styleArgs = React.useMemo<TextControlBase.TextControlBaseArgs>(() => ({
    block,
    border,
    size,
    indicator: Boolean(indicator),
    style,
    resizable: true,
    predefinedValues: false,
  }), [block, border, size, indicator, style]);

  const renderEnhanced = React.useMemo(() => enhanced && hydrated, [enhanced, hydrated]);

  const handleInput: React.FormEventHandler<HTMLInputElement> = (e) => {
    const target = e.target as HTMLInputElement;
    setViewValue(target.value.split(separator))
    onInput?.(e)
  }

  const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => {
    setFocused(true);
    onFocus?.(e);
  }

  const handleBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
    setFocused(false);
    onBlur?.(e);
  }

  const handleSelect: React.ReactEventHandler<HTMLInputElement> = (e) => {
    const target = e.currentTarget;
    const newSelectionStart = target.selectionStart ?? 0;
    const newSelectionEnd = typeof target.selectionEnd === 'number' ? target.selectionEnd : 0;
    const newDirection = Math.sign(newSelectionStart - selectionStart) > 0 ? 'forward' : 'backward'
    setSelectionStart(newSelectionStart);
    setSelectionEnd(newSelectionEnd);

    console.log(newDirection);

    const lastSeparatorIndex = target.value.lastIndexOf(separator);
    const separatorStartRaw = newSelectionStart > lastSeparatorIndex ? newSelectionStart : target.value.slice(0, newSelectionEnd).lastIndexOf(separator) + separator?.length;
    const separatorEndRaw = newSelectionEnd > lastSeparatorIndex ? newSelectionEnd : target.value.slice(0, newSelectionEnd).length + target.value.slice(newSelectionEnd).indexOf(separator);

    let separatorStart = 0;
    let separatorEnd;
    if (lastSeparatorIndex > -1) {
      separatorStart = separatorStartRaw > -1 ? separatorStartRaw : 0;
    }
    separatorEnd = separatorEndRaw;
    if (newSelectionStart <= target.value.lastIndexOf(separator)) {
      if (newSelectionStart === newSelectionEnd && newSelectionStart === separatorStart && newDirection === 'backward') {
        target.selectionStart = newSelectionStart - separator?.length;
        target.selectionEnd = newSelectionStart - separator?.length;
        target.selectionDirection = newDirection;
      } else if (newSelectionStart === newSelectionEnd && newSelectionEnd === separatorEnd && newDirection === 'forward') {
        target.selectionStart = newSelectionEnd + separator?.length;
        target.selectionEnd = newSelectionEnd + separator?.length;
        target.selectionDirection = newDirection;
      } else {
        target.selectionStart = separatorStart;
        target.selectionEnd = separatorEnd;
        target.selectionDirection = 'backward';
      }
    }
    if (onSelect) {
      onSelect(e);
    }
  }

  const focusOnInput: React.MouseEventHandler<HTMLDivElement> = (e) => {
    e.preventDefault();
    if (typeof effectiveRef === 'function') {
      return;
    }
    if (effectiveRef !== null && effectiveRef.current) {
      effectiveRef.current.focus();
    }
  }

  const tags = React.useMemo(() => viewValue.slice(0, -1), [viewValue])
  const inputText = React.useMemo(() => viewValue.slice(-1)[0] ?? '', [viewValue])

  React.useEffect(() => {
    setHydrated(true)
  }, []);

  return (
    <div
      className={TextControlBase.Root(styleArgs)}
    >
      <input
        {...etcProps}
        className={TextControlBase.Input(styleArgs)}
        ref={effectiveRef}
        aria-label={label as string}
        style={{
          height: TextControlBase.MIN_HEIGHTS[size],
          position: renderEnhanced ? 'absolute' : undefined,
          left: renderEnhanced ? -999999 : undefined,
        }}
        data-testid="input"
        onInput={handleInput}
        onFocus={handleFocus}
        onBlur={handleBlur}
        onSelect={handleSelect}
      />
      <div
        className={TextControlBase.Input(styleArgs)}
        onClick={focusOnInput}
        style={{
          cursor: 'text',
        }}
      >
        <div
          style={{
            margin: '-0.125rem',
          }}
        >
          {tags.map(v => (
            <div
              style={{
                padding: '0.125rem',
                display: 'inline-block',
              }}
              key={v}
            >
              <button
                className={BadgeBase.Root({ rounded: false })}
                style={{
                  border: 0,
                  font: 'inherit',
                  lineHeight: 0,
                  paddingTop: 0,
                  paddingBottom: 0,
                  color: 'inherit',
                  backgroundColor: 'transparent',
                }}
              >
                <div
                  className={BadgeBase.Content()}
                >
                  {v}
                  {' '}
                  &times;
                </div>
              </button>
            </div>
          ))}
          {
            inputText.lastIndexOf(separator) < 0
            && (
              <>
                {
                  inputText.slice(0, selectionStart - tags.join(separator).length - separator?.length)
                }
                <div
                  style={{
                    display: 'inline-block',
                    verticalAlign: 'middle',
                    backgroundColor: focused ? 'Highlight' : undefined,
                    color: focused ? 'HighlightText' : undefined,
                    height: '1.25em',
                    minWidth: 1,
                  }}
                >
                  {
                    inputText.slice(selectionStart - tags.join(separator).length - separator?.length, selectionEnd - tags.join(separator).length - separator?.length)
                  }
                </div>
                {
                  inputText.slice(selectionEnd - tags.join(separator).length - separator?.length)
                }
              </>
            )
          }
          {
            inputText.lastIndexOf(separator) >= 0
            && (
              <>
                {
                  inputText.slice(0, selectionStart - tags.join(separator).length)
                }
                <div
                  style={{
                    display: 'inline-block',
                    verticalAlign: 'middle',
                    backgroundColor: focused ? 'Highlight' : undefined,
                    color: focused ? 'HighlightText' : undefined,
                    height: '1.25em',
                    minWidth: 1,
                  }}
                >
                  {
                    inputText.slice(selectionStart - tags.join(separator).length - 1, selectionEnd - tags.join(separator).length - 1)
                  }
                </div>
                {
                  inputText.slice(selectionEnd - tags.join(separator).length - 1)
                }
              </>
            )
          }
        </div>
      </div>
      {
        border && (
          <span
            data-testid="border"
          />
        )
      }
      {
        label && !hiddenLabel && (
          <div
            data-testid="label"
            className={TextControlBase.LabelWrapper(styleArgs)}
          >
            {label}
          </div>
        )
      }
      {hint && (
        <div
          className={TextControlBase.HintWrapper(styleArgs)}
          data-testid="hint"
        >
          <div
            className={TextControlBase.Hint()}
          >
            {hint}
          </div>
        </div>
      )}
      {indicator && (
        <div
          className={TextControlBase.IndicatorWrapper(styleArgs)}
        >
          {indicator}
        </div>
      )}
    </div>
  );
});

TagInput.displayName = 'TagInput';
