'use client';

import { getClassNames, withSchema } from '@websolutespa/bom-core';
import { useCurrentState } from '@websolutespa/bom-mixer-hooks';
import React, { CSSProperties, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import { Ellipsis, Flex, getChildsByType } from '../../components';
import { UIComponentWithRef, UIStyledComponentProps } from '../../components/types';
import { getCssResponsive } from '../../components/utils';
import { SelectLegacyIcon } from '../select-legacy/select-legacy-icon';
import { FormControlCss } from '../styles';
import { CustomSelectConfig, CustomSelectContext } from './custom-select-context';
import { CustomSelectDivider } from './custom-select-divider';
import { CustomSelectDropdown } from './custom-select-dropdown';
import { CustomSelectInput } from './custom-select-input';
import { CustomSelectLabel } from './custom-select-label';
import { CustomSelectMultipleValue } from './custom-select-multiple-value';
import { CustomSelectOption } from './custom-select-option';

export type CustomSelectRef = {
  focus: () => void;
  blur: () => void;
  scrollTo?: (options?: ScrollToOptions) => void;
};

type Props = {
  disabled?: boolean;
  name?: string;
  value?: string | string[];
  initialValue?: string | string[];
  placeholder?: React.ReactNode | string;
  icon?: React.ComponentType;
  onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
  onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
  onChange?: (value: string | string[]) => void;
  pure?: boolean;
  multiple?: boolean;
  clearable?: boolean;
  className?: string;
  dropdownClassName?: string;
  dropdownStyle?: CSSProperties;
  disableMatchWidth?: boolean;
  onDropdownVisibleChange?: (visible: boolean) => void;
  getPopupContainer?: () => HTMLElement | null;
};

export type CustomSelectProps = UIStyledComponentProps<Props>;

export type CustomSelectComponent<C extends React.ElementType = 'select'> = UIComponentWithRef<C, Props>;

const StyledCustomSelect = styled.div<CustomSelectProps>`
  ${FormControlCss}

  padding: var(--form-control-padding);
  position: relative;
  display: inline-flex;
  justify-content: space-between;
  align-items: center;
  overflow: hidden;
  white-space: nowrap;
  appearance: none;
  user-select: none;
  cursor: pointer;

  .placeholder {
    color: inherit;
    opacity: 0.5;
  }

  .value {
    display: inline-flex;
    flex: 1;
    height: 100%;
    align-items: center;
    // line-height: 1;
    padding: 0;
    margin-right: 1.25em;
    width: calc(100% - 1.25em);
  }

  .icon {
    pointer-events: none;
    transition: color 200ms ease, transform 200ms ease;
  }

  &.opened {
    .icon {
      transform: rotate(180deg);
    }
  }

  &.multiple {
    padding: calc(var(--form-control-padding) * 0.675);

    .placeholder {
      padding: 0.563rem 0;
    }
  }

  &.disabled {
    .value,
    .icon {
      color: var(--color-primary-400);
    }
  }

  ${props => getCssResponsive(props)}
`;

const CustomSelectBase: CustomSelectComponent = forwardRef<CustomSelectRef, React.PropsWithChildren<CustomSelectProps>>(({
  disabled = false,
  pure = false,
  multiple = false,
  clearable = true,
  className = '',
  disableMatchWidth = false,
  onDropdownVisibleChange = () => { },
  value: valueProp,
  initialValue: initialValueProp,
  icon,
  children,
  onChange,
  placeholder,
  dropdownClassName,
  dropdownStyle,
  getPopupContainer,
  onBlur,
  onFocus,
  id,
  name,
  ...props
}: React.PropsWithChildren<CustomSelectProps>, selectRef) => {

  const Icon = icon || SelectLegacyIcon;

  const ref = useRef<HTMLDivElement>(null);

  const inputRef = useRef<HTMLInputElement>(null);

  const dropdownRef = useRef<HTMLDivElement>(null);

  const [visible, setVisible] = useState<boolean>(false);

  const [selectFocus, setSelectFocus] = useState<boolean>(false);

  const [value, setValue, valueRef] = useCurrentState<string | string[]>(() => {
    if (!multiple) {
      return typeof initialValueProp === 'undefined' ? '' : initialValueProp;
    }
    if (Array.isArray(initialValueProp)) {
      return initialValueProp;
    }
    return typeof initialValueProp === 'undefined' ? [] : [initialValueProp];
  });

  const isEmpty = useMemo(() => {
    if (!Array.isArray(value)) {
      return !value;
    }
    return value.length === 0;
  }, [value]);

  const updateVisible = useCallback((next: boolean) => {
    onDropdownVisibleChange(next);
    setVisible(next);
  }, [onDropdownVisibleChange]);

  const updateValue = (next: string) => {
    setValue(last => {
      if (!Array.isArray(last)) {
        return next;
      }
      if (!last.includes(next)) {
        return [...last, next];
      }
      return last.filter(item => item !== next);
    });
    onChange && onChange(valueRef.current as string | string[]);
    if (!multiple) {
      updateVisible(false);
    }
  };

  const initialValue: CustomSelectConfig = useMemo(() => ({
    disableAll: disabled,
    visible,
    value,
    ref,
    updateValue,
    updateVisible,
  }), [disabled, visible, value]);

  const clickHandler = (event: React.MouseEvent<HTMLDivElement>) => {
    event.stopPropagation();
    event.nativeEvent.stopImmediatePropagation();
    event.preventDefault();
    if (disabled) {
      return;
    }
    updateVisible(!visible);
  };

  const mouseDownHandler = (event: React.MouseEvent<HTMLDivElement>) => {
    /* istanbul ignore next */
    if (visible) {
      event.preventDefault();
    }
  };

  useEffect(() => {
    if (valueProp === undefined) {
      return;
    }
    setValue(valueProp);
  }, [setValue, valueProp]);

  useImperativeHandle(selectRef, () => ({
    focus: () => inputRef.current?.focus(),
    blur: () => inputRef.current?.blur(),
    scrollTo: options => dropdownRef.current?.scrollTo(options),
  }), [inputRef, dropdownRef]);

  const selectedChildren = (() => {
    const onClear = (value: string) => {
      // console.log('onClear', value, clearable);
      if (clearable) {
        updateValue(value);
      }
    };
    const values = Array.isArray(value) ? value : [value];
    const [options] = getChildsByType(children, CustomSelectOption);
    return React.Children.map(options, (option: any) => { // !!! any
      const optionValue = option.props.value;
      const optionLabel = option.props.children;
      if (!values.includes(optionValue)) {
        return null;
      }
      if (!multiple) {
        return optionLabel;
      }
      return (
        <CustomSelectMultipleValue disabled={disabled} onClear={() => onClear(optionValue)}>
          {optionLabel}
        </CustomSelectMultipleValue>
      );
    });
  })();

  const onFocus_ = (event: React.FocusEvent<HTMLInputElement>) => {
    onFocus && onFocus(event);
    setSelectFocus(true);
  };

  const onBlur_ = (event: React.FocusEvent<HTMLInputElement>) => {
    onBlur && onBlur(event);
    updateVisible(false);
    setSelectFocus(false);
  };

  const classNames = getClassNames('select', { active: selectFocus || visible, opened: visible, multiple, disabled }, className);

  return (
    <CustomSelectContext.Provider value={initialValue}>
      <StyledCustomSelect
        ref={ref}
        className={classNames}
        onClick={clickHandler}
        onMouseDown={mouseDownHandler}
        {...props}
      >
        <CustomSelectInput ref={inputRef} id={id} name={name} visible={visible} onFocus={onFocus_} onBlur={onBlur_} />
        {isEmpty && (
          <span className="value placeholder">
            <Ellipsis>{placeholder}</Ellipsis>
          </span>
        )}
        {value && (
          multiple ?
            <Flex.Row flexWrap="wrap" maxWidth="calc(100% - 1.5rem)" minHeight="2.2em">{selectedChildren}</Flex.Row> :
            <span className="value">{selectedChildren}</span>
        )}
        <CustomSelectDropdown ref={dropdownRef} className={dropdownClassName} visible={visible} dropdownStyle={dropdownStyle} disableMatchWidth={disableMatchWidth} getPopupContainer={getPopupContainer}>
          {children}
        </CustomSelectDropdown>
        {!pure && (
          <Icon className="icon" />
        )}
      </StyledCustomSelect>
    </CustomSelectContext.Provider>
  );
});

CustomSelectBase.displayName = 'CustomSelect';

export const CustomSelect = withSchema(
  CustomSelectBase,
  {
    Label: CustomSelectLabel,
    Divider: CustomSelectDivider,
    Option: CustomSelectOption,
  });
