import React, { useRef, useState } from 'react';
import { VevCondition } from '@vev/interactions';
import { VevProps } from '@vev/utils';
import {
  SilkeTextFieldOutline,
  SilkeTextFieldOutlineProps,
} from '../silke-text-field/silke-text-field-outline';
import { SilkeBox } from '../silke-box';
import { SilkeText } from '../silke-text';
import { SilkeTextFieldItem } from '../silke-text-field';
import { SilkePopover } from '../silke-popover';
import { SilkeConditionsInputOptions } from './silke-conditions-input-options';
import { ConditionSuggestion, VevConditionSuggestion } from './types';
import { useSilkeCSSContext } from '../silke-css-number-field';
import { SilkeConditionsTag } from './silke-conditions-tag';
import {
  booleanSuggestions,
  getCombinatorSuggestions,
  getOperatorSuggestions,
  getSuggestions,
  getSystemSuggestionOptions,
  getWidgetOptionType,
  getWidgetSuggestionOptions,
} from './utils';
import { SilkeButton } from '../silke-button';
import styles from './silke-conditions-input.scss';

export type SilkeConditionsInputProps = SilkeTextFieldOutlineProps & {
  conditions: VevCondition;
  placeholder?: string;
  label?: string;
  systemOptions?: VevConditionSuggestion[];
  widgetOptions?: VevProps[];
  disabled?: boolean;
  onClear?: () => void;
  onChange: (value: VevCondition) => void;
};

export function SilkeConditionsInput(props: SilkeConditionsInputProps) {
  const { conditions = [], systemOptions, widgetOptions, placeholder, disabled, onChange } = props;
  const [value, setValue] = useState('');
  const [showSuggestions, setShowSuggestions] = useState(false);
  const [highlightedSuggestion, setHighlightedSuggestion] = useState<number>(-1);
  const wrapperRef = useRef<HTMLDivElement>(null);
  const context = useSilkeCSSContext();
  const variables = context.variablesv2;

  // Determine if we're inputting a, operator or b (or a combinator)
  const currentInputType = React.useMemo(() => {
    if (conditions.length % 4 === 0) return 'a';
    if (conditions.length % 4 === 1) return 'operator';
    if (conditions.length % 4 === 2) return 'b';
    return 'combinator';
  }, [conditions]);

  // Get suggestions based on current input type
  const suggestionItems: ConditionSuggestion[] = React.useMemo(() => {
    // Check if we should display a list of options (if a is of type select)
    if (currentInputType === 'b') {
      const aValue = conditions[conditions.length - 2];
      const systemItems = getSystemSuggestionOptions(aValue, systemOptions);
      if (systemItems && systemItems.length > 0) {
        return systemItems;
      }
      const widgetItems = getWidgetSuggestionOptions(aValue, widgetOptions);
      if (widgetItems && widgetItems.length > 0) {
        return widgetItems;
      }

      // Check if A value is of type boolean, if so return true/false as suggestions
      if (getWidgetOptionType(aValue, widgetOptions) === 'boolean') {
        return booleanSuggestions;
      }

      // If we have a value, but no suggestions, return any variable suggestions of same type
      return getSuggestions(value || '', variables);
    }

    if (currentInputType === 'combinator') {
      return getCombinatorSuggestions(value || '');
    }
    if (currentInputType === 'operator') {
      // Check if A value is of type boolean, if so limit operators to is/is not
      const onlyTrueFalse =
        getWidgetOptionType(conditions[conditions.length - 1], widgetOptions) === 'boolean';
      return getOperatorSuggestions(value || '', onlyTrueFalse);
    }

    return getSuggestions(value || '', variables, systemOptions, widgetOptions);
  }, [currentInputType, value, conditions, variables, systemOptions, widgetOptions]);

  const handleBackspace = (e: React.KeyboardEvent<HTMLDivElement>) => {
    // If we have a value in the input field, abort!
    if (value) return;

    // Otherwise, delete last item in the condition
    e.stopPropagation();
    e.preventDefault();

    // Check if command key is pressed
    if (e.metaKey) {
      onClear();
      return;
    }
    const newConditions = conditions.slice(0, -1);
    onChange(newConditions);
  };

  // A new item has been selected/added
  const handleAddItem = (item: string | number | boolean, isBlurEvent?: boolean) => {
    const newConditions = [...conditions, item];
    onChange(newConditions);

    // Set focus back to input (if not a blur event)
    if (!isBlurEvent) focusInput();
  };

  // A new suggestion has been selected/added
  const handleAddSuggestion = (item: ConditionSuggestion, isBlurEvent?: boolean) => {
    const newItems = [typeof item === 'string' ? item : item.value];
    const prefix = typeof item === 'string' ? null : item.prefix || null;
    const suffix = typeof item === 'string' ? null : item.suffix || null;
    if (prefix) newItems.unshift(prefix);
    if (suffix) newItems.push(suffix);

    const newConditions = [...conditions, ...newItems];
    onChange(newConditions);

    // Reset input value
    onChangeValue('');

    // Set focus back to input (if not a blur event)
    if (!isBlurEvent) focusInput();
  };

  const focusInput = () => {
    // Find <input> element in SilkeTextField
    const input = wrapperRef.current?.querySelector('input');
    if (input) {
      setTimeout(() => input.focus());
    }
  };

  const onChangeValue = (newValue: string) => {
    setValue(newValue);
    setHighlightedSuggestion(1);
  };

  const onClear = () => {
    onChange([]);
    props.onClear?.();
  };

  const selectNext = React.useCallback(() => {
    if (highlightedSuggestion === -1 || highlightedSuggestion + 1 >= suggestionItems.length) {
      setHighlightedSuggestion(1);
    } else {
      if (typeof suggestionItems[highlightedSuggestion + 1] === 'string') {
        if (highlightedSuggestion + 2 >= suggestionItems.length) {
          setHighlightedSuggestion(1);
        } else {
          setHighlightedSuggestion(highlightedSuggestion + 2);
        }
      } else {
        setHighlightedSuggestion(highlightedSuggestion + 1);
      }
    }
  }, [highlightedSuggestion, suggestionItems]);

  const selectPrev = React.useCallback(() => {
    if (highlightedSuggestion === -1 || highlightedSuggestion - 1 === -1) {
      setHighlightedSuggestion(suggestionItems.length - 1);
    } else {
      if (typeof suggestionItems[highlightedSuggestion - 1] === 'string') {
        if (highlightedSuggestion - 2 < 0) {
          setHighlightedSuggestion(suggestionItems.length - 1);
        } else {
          setHighlightedSuggestion(highlightedSuggestion - 2);
        }
      } else {
        setHighlightedSuggestion(highlightedSuggestion - 1);
      }
    }
  }, [highlightedSuggestion, suggestionItems]);

  const onConfirmSelected = () => {
    const suggestedItem = suggestionItems[highlightedSuggestion];
    if (suggestedItem) {
      handleAddSuggestion(suggestedItem);
      onChangeValue('');
    } else if (value) {
      handleAddItem(value);
      onChangeValue('');
    }
  };

  const onFocusInput = () => {
    setShowSuggestions(true);
    setHighlightedSuggestion(1);
  };

  const onBlurInput = () => {
    // Check if we should add the current value as a new item
    if (value) {
      handleAddItem(value, true);
      onChangeValue('');
    }

    setShowSuggestions(false);
    setHighlightedSuggestion(-1);
  };

  return (
    <SilkeBox style={{ width: '100%' }}>
      <SilkeTextFieldOutline onClick={focusInput} ref={wrapperRef} size="s" {...props}>
        <SilkeBox className={styles.inner} wrap align="start" fill vPad="xs" hPad="s">
          <SilkeTextFieldItem>
            <SilkeText color="neutral-60" size="small">
              If
            </SilkeText>
          </SilkeTextFieldItem>
          {conditions?.map((condition, index) => (
            <SilkeConditionsTag
              key={index}
              condition={condition}
              variables={variables}
              widgetOptions={widgetOptions}
              systemOptions={systemOptions}
            />
          ))}

          <input
            value={value}
            className={styles.field}
            placeholder={!value && !conditions.length ? placeholder : undefined}
            disabled={disabled}
            data-empty={!value && !conditions.length}
            data-hidden={!showSuggestions}
            onChange={(e) => onChangeValue(e.target.value)}
            onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {
              switch (e.key) {
                case 'Enter': {
                  onConfirmSelected();
                  break;
                }
                case ' ': {
                  e.preventDefault();
                  onConfirmSelected();
                  break;
                }
                case 'ArrowUp':
                  selectPrev();
                  break;
                case 'ArrowDown': {
                  selectNext();
                  break;
                }
                case 'Backspace': {
                  handleBackspace(e);
                  break;
                }
              }
            }}
            onFocus={onFocusInput}
            onBlur={onBlurInput}
          />
        </SilkeBox>

        {Boolean(conditions.length) && (
          <SilkeTextFieldItem vAlign="start" hPad="xs" vPad="xs">
            <SilkeButton size="s" icon="close" kind="ghost" onClick={onClear} />
          </SilkeTextFieldItem>
        )}
      </SilkeTextFieldOutline>

      <SilkePopover
        hide={!showSuggestions}
        anchor={wrapperRef}
        anchorOrigin="bottom-left"
        targetOrigin="top-left"
        matchAnchorWidth
      >
        <SilkeConditionsInputOptions
          suggestions={suggestionItems}
          highlightedItem={highlightedSuggestion}
          onSelect={handleAddSuggestion}
        />
      </SilkePopover>
    </SilkeBox>
  );
}

SilkeConditionsInput.displayName = 'SilkeConditionsInput';
