import { EmojiType, IEmojiIcon } from '@vev/utils';
import { isEqual } from 'lodash';
import React, {
  Children,
  KeyboardEvent,
  ReactNode,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { useFormField, useUniqId } from '../../hooks';
import { BoxSize, SilkeBox } from '../silke-box';
import { FormValidator } from '../silke-form';
import { SilkeIcon, SilkeIcons } from '../silke-icon';
import { PopoverOrigin } from '../silke-popover';
import { SilkeText, SilkeTextSmall } from '../silke-text';
import { SilkeTextFieldItem, SilkeTextFieldProps } from '../silke-text-field';
import { SilkeTextFieldOutline } from '../silke-text-field/silke-text-field-outline';
import { useTextFieldContext } from '../silke-text-field/text-field-context';
import { AutocompleteRenderItemProps } from './autocomplete-item';
import AutocompleteTags, { AutocompleteTagsProps } from './autocomplete-tags';
import { Data, Entry, MultiProps, SingleProps } from './autocomplete-types';
import { SilkeAutocompletePopupNative } from './silke-autocomplete-popup-native';
import styles from './silke-autocomplete-field.scss';

/**
 * Props for autocomplete field
 */
export type SilkeAutocompleteFieldNativeProps<T = any, U extends Entry<T> = any> = (
  | MultiProps<T>
  | SingleProps<T>
) &
  Omit<SilkeTextFieldProps, 'multiline' | 'maxLength' | 'minLength' | 'value'> & {
    disableSearch?: boolean;
    disableAddFromList?: boolean;
    noReset?: boolean;
    hideListArrow?: boolean;
    isVariable?: boolean;
    maxDisplayedItems?: number;
    maxDisplayedItemsLabel?: React.ReactNode;
    showAddIcon?: boolean;
    selectedVariableKey?: string;

    /** What point of the target should be used for centering default center-center  */
    targetOrigin?: PopoverOrigin;
    /** What point of the anchor should be used for centering default center-center  */
    anchorOrigin?: PopoverOrigin;

    dropDownMinWidth?: number;
    dropDownMaxWidth?: number;
    /**
     * Possible values
     */
    items: U[];
    /**
     * Minimum number of characters in search for results to show
     */
    minChars?: number;
    buttonRight?: ReactNode;
    /** Cypress tag */
    'data-cy'?: string;
    /**
     * If a function is passed, an "Add item" entry will be shown in the list if nothing matches text search. This function will be the callback.
     * @param value The value entered
     */
    onAdd?: (value: string) => void;
    /**
     * Use custom component for rendering the autocomplete items
     */
    renderItem?: (props: AutocompleteRenderItemProps<U>) => React.ReactElement | null;
    /**
     * Use custom component for rendering autocomplete tags
     */
    renderTags?: (props: AutocompleteTagsProps<T>) => React.ReactElement | null;
    /**
     * Custom sort of items (default sorts by label)
     */
    sort?: (one: Entry<T>, other: Entry<T>) => number;
    onSearch?: (value: string) => void;
    /**
     * Called when dropdown closes for any reason
     */
    onClose?: () => void;
  };

const autocompleteValidator: FormValidator<SilkeAutocompleteFieldNativeProps> = (props, value) => {
  if (props.multiple) {
    const len = value?.length || 0;
    if (len > (props.maxItems || 1000)) return `To many item, max ${props.maxItems}`;
    if (len < (props.minItems || 0)) return `To few item, min ${props.minItems}`;
  }
  if (props.required && !value && value !== 0) return 'Required';

  return undefined;
};

export function SilkeAutocompleteFieldNative<T>(props: SilkeAutocompleteFieldNativeProps<T>) {
  const {
    hideListArrow,
    isVariable,
    items,
    placeholder,
    renderTags: TagsRender = AutocompleteTags,
    label,
    inline,
    flex,
    disabled,
    tabIndex,
    onClick,
    onMouseDown,
    hAlign,
    onClose,
    autoFocus,
    ...form
  } = useFormField(props, autocompleteValidator);
  const context = useTextFieldContext();
  const size = form.size || context.size || 'base';
  const uid = useUniqId('silke-input');
  const inputId = form.id || uid;

  const inputField = useRef<HTMLDivElement>();
  const isSelected = (value: T): boolean => {
    if (form.multiple) {
      for (const v of Array.isArray(form.value) ? form.value : []) {
        if (isEqual(v, value)) return true;
      }
      return false;
    }
    return isEqual(value, form.value);
  };

  /** This list contains all *data* entries, dividers are stripped */
  const dataItems = items.filter((entry) => !entry.divider) as Data<T>[];
  const selectedDataItems = dataItems.filter((item) => isSelected(item.value));

  const [selectedLabel, selectedIcon, selectedImage] = useMemo((): [
    label?: string,
    icon?: SilkeIcons | IEmojiIcon,
    image?: string,
  ] => {
    if (form.multiple) return [];
    const entry = items.find((entry) => !entry.divider && isEqual(entry.value, form.value));

    return [entry?.label || (form.value && form.value.toString()) || '', entry?.icon, entry?.image];
  }, [items, form.multiple, form.value]);

  const containerRef = useRef<HTMLDivElement>(null);
  const [open, setOpen] = useState(false);

  // Prevent clicks on the anchor from closing the popover
  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;

    const handleDocumentClick = (e: MouseEvent) => {
      // If click is within the anchor container and popover is open, prevent it from closing
      if (open && container.contains(e.target as Node)) {
        e.stopPropagation();
        e.stopImmediatePropagation();
      }
    };

    // Use capture phase to catch the event before it reaches the popover
    document.addEventListener('click', handleDocumentClick, true);

    return () => {
      document.removeEventListener('click', handleDocumentClick, true);
    };
  }, [open]);

  const before: ReactNode[] = [];
  const after: ReactNode[] = [];
  Children.forEach(form.children, (child) => {
    if (React.isValidElement(child) && child.type === SilkeTextFieldItem && child.props.before) {
      before.push(child);
    } else {
      after.push(child);
    }
  });

  const handleReset = (e?: React.MouseEvent<HTMLButtonElement>) => {
    e?.stopPropagation();

    if (form.multiple) form.onChange([]);
    else form.onChange('');
    onClose && onClose();
    setOpen(false);
  };

  const handleRemoveSingle = (remove: T) => {
    if (form.multiple && form.value) {
      const value = Array.isArray(form.value) ? form.value.filter((v: T) => remove !== v) : [];
      if (!isEqual(value, form.value)) form.onChange(value);
    }
  };

  const displayReset =
    props.multiple && !form.noReset && Array.isArray(form.value) && form.value.length > 0;
  const dataCy = props['data-cy'] || undefined;
  const kind = props?.kind || context.kind || 'default';

  const boxSize: BoxSize = size === 's' ? 's' : 'base';
  let dropDownSize = size;
  if (kind !== 'default') dropDownSize = 's';
  else if (size === 's') dropDownSize = 's';

  let cl = styles.root;
  if (open) cl += ' ' + styles.open;
  if (styles[kind]) cl += ' ' + styles[kind];
  if (disabled) cl += ' ' + styles.disabled;
  if (isVariable) cl += ' ' + styles.variable;

  return (
    <>
      <SilkeTextFieldOutline
        ref={containerRef}
        inline={inline}
        size={size}
        flex={flex}
        label={label}
        className={cl}
        error={form.error}
        help={form.help}
        width={form.width}
        disabled={disabled}
        kind={kind}
        hAlign={hAlign}
        data-cy={dataCy}
        labelId={inputId}
        labelUsingAria
        onClick={(e: React.MouseEvent<HTMLElement>) => {
          e.stopPropagation();
          onClick?.(e);
          if (disabled) return;
          if (!context.readOnly) {
            setOpen((prevOpen) => !prevOpen);
          }
        }}
        onMouseDown={(e: React.MouseEvent<HTMLElement>) => {
          e.preventDefault();
          e.stopPropagation();
          onMouseDown?.(e);
        }}
      >
        {before}

        <SilkeTextFieldItem
          minHeight={boxSize}
          vAlign="center"
          tag="button"
          type="button"
          autoFocus={autoFocus}
          name={form.name}
          value={form.value as string}
          color={context.color}
          aria-labelledby={inputId}
          hAlign="start"
          data-input="true"
          data-cy={form['data-cy']}
          tabIndex={tabIndex}
          style={{
            padding: size === 's' ? 2 : undefined,
            paddingLeft: selectedIcon || selectedImage ? 0 : undefined,
            flex: kind !== 'ghost' && kind !== 'zombie' ? undefined : 'none',
            maxWidth: `calc(100% - ${size === 's' ? 16 : 24}px)`,
          }}
          ref={inputField}
          data-placeholder={placeholder}
          onKeyUp={(e: KeyboardEvent) => {
            if ((e.key === 'Enter' || e.key === 'ArrowDown') && !open && !disabled) {
              e.preventDefault();
              setOpen(true);
            }
          }}
          onFocus={() => {
            if (!open && !disabled) setOpen(true);
          }}
        >
          {form.multiple && Array.isArray(form.value) && form.value.length > 0 ? (
            <SilkeBox vPad={size === 's' ? 'xxs' : 'xs'} gap="xxs">
              <TagsRender
                items={selectedDataItems}
                showMaxTags={form.showMaxTags}
                tiny={size === 's'}
                onRemove={handleRemoveSingle}
                onRemoveAll={handleReset}
              />
            </SilkeBox>
          ) : (
            <>
              {selectedImage && (
                <SilkeBox width={dropDownSize} align="center" hPad="xs">
                  <SilkeIcon icon={{ type: EmojiType.upload, value: { url: selectedImage } }} />
                </SilkeBox>
              )}

              {selectedIcon && (
                <SilkeBox width={dropDownSize} align="center" hPad="xs">
                  <SilkeIcon icon={selectedIcon} />
                </SilkeBox>
              )}
              {selectedLabel && size === 's' && (
                <SilkeTextSmall overflow>{selectedLabel}</SilkeTextSmall>
              )}
              {selectedLabel && size !== 's' && <SilkeText overflow>{selectedLabel}</SilkeText>}
              {placeholder && !selectedLabel && (
                <SilkeText overflow className={styles.placeholder}>
                  {placeholder}
                </SilkeText>
              )}
            </>
          )}
        </SilkeTextFieldItem>

        {(displayReset || !hideListArrow) && (
          <SilkeTextFieldItem
            align="center"
            width={dropDownSize}
            className={styles.downIcon}
            onClick={displayReset ? handleReset : undefined}
          >
            {(() => {
              return displayReset ? (
                <SilkeBox width="xs" height="s" align="center">
                  <SilkeIcon icon="close" />
                </SilkeBox>
              ) : !hideListArrow ? (
                <SilkeBox align="center" className={styles.arrow}>
                  <SilkeIcon icon={open ? 'chevron.up' : 'chevron.down'} />
                </SilkeBox>
              ) : null;
            })()}
          </SilkeTextFieldItem>
        )}
        {after}
      </SilkeTextFieldOutline>
      <SilkeAutocompletePopupNative
        containerRef={containerRef}
        hide={!open}
        onRequestClose={() => setOpen(false)}
        {...props}
      />
    </>
  );
}
