import React, { forwardRef, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import classNames from 'clsx';

import {
  CATEGORY_SIZE,
  CONTEXT_OPEN_IN_TAG_LOG,
  CONTEXT_OPEN_IN_TAG_LOG_SUMMARY,
  CUSTOM_TAGS_COLORS,
  SYSTEM_TAGS_CATEGORY_OPTIONS,
  TAG_CUSTOM_TYPE,
} from '../../../../constants';

import {
  customCategoriesOptionsSelector,
  openContextSelector,
  selectedCategorySelector,
} from '../../../../redux/selectors';
import { createCustomCategoryAction, getCustomCategoriesOptionsAction, setStateAction } from '../../../../redux/slices';
import { excludeCategoryBy } from '@/modules/editorial/TagEditor/helpers';

import CreateCategory from './CreateCategory';
import { Autocomplete, Box, InputAdornment, TextField, Typography } from '@/mui/components';
import { CustomLabelTag } from '@/mui/components/CustomIcon';

import styles from './SelectCreateCategory.module.scss';

function normalizeCustomCategories(categories) {
  if (!categories) return [];
  return categories.map((category) => ({
    ...category,
    iconComponent: CustomLabelTag,
    group: TAG_CUSTOM_TYPE,
  }));
}

function checkIsExistInOptions(newCategoryValue, options) {
  const normalize = (label) => label.trim().toLowerCase();
  return options.some((o) => normalize(o.label) === normalize(newCategoryValue));
}

const initialNewCustomCategoryValue = {
  label: '',
  value: '',
  color: CUSTOM_TAGS_COLORS[0],
  iconComponent: CustomLabelTag,
  group: TAG_CUSTOM_TYPE,
};

const ListboxComponent = forwardRef((props, ref) => {
  const {
    // eslint-disable-next-line react/prop-types
    newCustomCategory,
    // eslint-disable-next-line react/prop-types
    handleCreateCategory,
    // eslint-disable-next-line react/prop-types
    options,
    // eslint-disable-next-line react/prop-types
    shouldShowCustomCategories,
    ...listBoxProps
  } = props;

  // eslint-disable-next-line react/prop-types
  const isExist = checkIsExistInOptions(newCustomCategory.value, options);

  // eslint-disable-next-line react/prop-types
  const tagNotExist = newCustomCategory.value && !isExist;

  return (
    <>
      <div className={styles.ListboxWrapper}>
        <ul {...listBoxProps} ref={ref} className={classNames(listBoxProps.className)} />
      </div>
      {shouldShowCustomCategories && tagNotExist && <CreateCategory onCreate={handleCreateCategory} />}
    </>
  );
});

function SelectCreateCategory(props) {
  const dispatch = useDispatch();
  const [InputAdornmentIconComponent, setInputAdornmentIconComponent] = useState(null);
  const [newCustomCategory, setNewCustomCategory] = useState(initialNewCustomCategoryValue);
  const [open, setOpen] = useState(false);

  const selectedCategory = useSelector(selectedCategorySelector);
  const customCategoriesOptions = useSelector(customCategoriesOptionsSelector);
  const openContext = useSelector(openContextSelector);

  // based on openContext
  const shouldShowCustomCategories = !props.shouldExcludeAllCustomTags;
  const placeholder =
    [CONTEXT_OPEN_IN_TAG_LOG_SUMMARY, CONTEXT_OPEN_IN_TAG_LOG].includes(openContext) || !shouldShowCustomCategories
      ? 'Select Category'
      : 'Select or Create Category';

  const customCategories = normalizeCustomCategories(customCategoriesOptions);

  const options = [...excludeCategoryBy(props.excludeSystemTagsCategory, SYSTEM_TAGS_CATEGORY_OPTIONS)];

  if (shouldShowCustomCategories) {
    options.push(...customCategories);
  }

  const handleGetCategories = () => {
    if (shouldShowCustomCategories) {
      dispatch(
        getCustomCategoriesOptionsAction({
          accountId: props.accountId,
        }),
      );
    }

    setOpen(true);
  };

  const handleClose = () => {
    setNewCustomCategory(initialNewCustomCategoryValue);
    setOpen(false);
  };

  const handleSetInputAdornmentIcon = (e, value, reason) => {
    if (reason === 'reset') {
      setInputAdornmentIconComponent(selectedCategory?.iconComponent);
      return;
    }

    if (shouldShowCustomCategories) {
      dispatch(
        getCustomCategoriesOptionsAction({
          accountId: props.accountId,
          prefix: value,
        }),
      );
    }

    const partialEqual = options.find((o) => o.label.includes(value));
    if (partialEqual) {
      setInputAdornmentIconComponent(null);
    } else if (shouldShowCustomCategories) {
      setInputAdornmentIconComponent(CustomLabelTag);
    }

    setNewCustomCategory((prev) => ({
      ...prev,
      value,
      label: value,
    }));
  };

  const handleSelectCategory = (category) => {
    dispatch(setStateAction({ selectedCategory: category }));

    if (category?.iconComponent) {
      setInputAdornmentIconComponent(category.iconComponent);
    }
  };

  const handleCreateCategory = (color) => {
    dispatch(
      createCustomCategoryAction({
        category: { ...newCustomCategory, color },
        accountId: props.accountId,
      }),
    );
    setOpen(false);
  };

  return (
    <Autocomplete
      fullWidth
      open={open}
      size="small"
      isLoading={!customCategoriesOptions}
      value={selectedCategory}
      options={options}
      groupBy={(option) => option.group}
      classes={{
        noOptions: shouldShowCustomCategories ? styles.NoOptionsReset : '',
      }}
      isOptionEqualToValue={(sourceOption, option) => {
        // custom tags
        if (sourceOption.id) {
          return sourceOption.id === option.id;
        }
        // system tags
        return sourceOption.value === option.value;
      }}
      renderInput={(params) => (
        <TextField
          {...params}
          size="small"
          variant="outlined"
          placeholder={placeholder}
          inputProps={{
            ...params.inputProps,
            maxLength: CATEGORY_SIZE,
          }}
          InputProps={{
            ...params.InputProps,
            startAdornment: InputAdornmentIconComponent ? (
              <InputAdornment position="start">
                <InputAdornmentIconComponent />
              </InputAdornment>
            ) : null,
          }}
        />
      )}
      renderGroup={(params) => (
        <Box>
          <Typography variant="overline" color="text.primary" className={styles.GroupName}>
            {params.group}
          </Typography>
          {params.children}
        </Box>
      )}
      ListboxComponent={(params) => (
        <ListboxComponent
          {...params}
          options={options}
          shouldShowCustomCategories={shouldShowCustomCategories}
          newCustomCategory={newCustomCategory}
          handleCreateCategory={handleCreateCategory}
        />
      )}
      renderOption={(optionProps, option) => {
        const IconComponent = option.iconComponent;
        return (
          <Box
            {...optionProps}
            key={`${option.label}${option.value}${option?.id}`}
            component="li"
            className={classNames(optionProps.className, styles.CustomOptions)}
          >
            <IconComponent fontSize="xSmall" className={styles.CustomOptionIcon} />
            <Typography variant="caption" color="text.primary">
              {option.label}
            </Typography>
          </Box>
        );
      }}
      noOptionsText={(() => {
        if (!shouldShowCustomCategories) {
          return 'No options';
        }

        const isExist = checkIsExistInOptions(newCustomCategory.value, options);
        return !isExist && shouldShowCustomCategories && <CreateCategory onCreate={handleCreateCategory} />;
      })()}
      disabled={props.disabled}
      onInputChange={handleSetInputAdornmentIcon}
      onOpen={handleGetCategories}
      onClose={handleClose}
      onChange={(e, category) => handleSelectCategory(category)}
    />
  );
}

SelectCreateCategory.propTypes = {
  // from props
  accountId: PropTypes.number.isRequired,
  excludeSystemTagsCategory: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
  shouldExcludeAllCustomTags: PropTypes.bool,
  disabled: PropTypes.bool.isRequired,
};

export default SelectCreateCategory;
