import { Fragment, type ReactElement } from 'react';
import { Controller, useFormContext } from 'react-hook-form';

import { merge, uniq } from 'lodash-es';
import CheckBoxIcon from '@material-hu/icons/material/CheckBox';
import CheckBoxOutlineBlankIcon from '@material-hu/icons/material/CheckBoxOutlineBlank';
import LocationIcon from '@material-hu/icons/material/LocationOnRounded';
import Autocomplete from '@material-hu/mui/Autocomplete';
import Checkbox from '@material-hu/mui/Checkbox';
import CircularProgress from '@material-hu/mui/CircularProgress';
import { type SxProps } from '@material-hu/mui/styles';
import TextField, { type TextFieldProps } from '@material-hu/mui/TextField';
import { createFilterOptions } from '@material-hu/mui/useAutocomplete';

import { type MUIAutocompleteProps } from '@material-hu/components/design-system/Inputs/Autocomplete/types';

const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
const mapIcon = (
  <LocationIcon sx={{ color: theme => theme.palette.text.secondary }} />
);
const filter = createFilterOptions();

export type Props = {
  name: string;
  textFieldProps?: TextFieldProps;
  autocompleteProps?: Partial<
    MUIAutocompleteProps<any, boolean, boolean, boolean>
  >;
  loading?: boolean;
  rules?: any;
  infinite?: ReactElement;
  query?: Function;
  sx?: SxProps;
  hideCheckbox?: boolean;
  showMapIcon?: boolean;
  endInputAdornment?: JSX.Element;
  clearOnBlur?: boolean;
  showErrorState?: boolean;
  avoidFiltering?: boolean;
  resetOnBlur?: boolean;
};

function FormAutocomplete({
  name,
  textFieldProps,
  autocompleteProps,
  clearOnBlur = true,
  loading,
  rules,
  infinite,
  query,
  hideCheckbox = false,
  sx,
  showMapIcon = false,
  endInputAdornment,
  showErrorState = true,
  avoidFiltering = false,
  resetOnBlur = false,
}: Props) {
  const { control } = useFormContext();

  const sanitizeValues = (array: unknown[]) => {
    let newArray = array.filter(
      value => !!(value as string).toString().trim().length,
    );
    newArray = uniq(newArray);
    return newArray;
  };

  return (
    <Controller
      render={({ field, fieldState }) => {
        const safeValue =
          autocompleteProps?.multiple && !Array.isArray(field.value)
            ? []
            : field.value;
        return (
          <Autocomplete
            {...field}
            value={safeValue}
            onInputChange={(_, value, reason) => query?.(value, reason)}
            onChange={(_, value) => field.onChange(value)} // support both multiple and single value
            disableCloseOnSelect={autocompleteProps?.multiple}
            limitTags={3}
            filterOptions={(o, s) => (avoidFiltering ? o : filter(o, s))}
            options={[]}
            renderOption={(
              { id, key, ...props },
              option: any,
              { selected, index },
            ) => (
              <Fragment key={`${id}-${key}`}>
                <li {...props}>
                  {!hideCheckbox && (
                    <Checkbox
                      icon={icon}
                      checkedIcon={checkedIcon}
                      style={{ marginRight: 8 }}
                      checked={selected}
                    />
                  )}
                  {showMapIcon && mapIcon}
                  {option.title ||
                    autocompleteProps?.getOptionLabel?.(option) ||
                    option}
                </li>
                {index === (autocompleteProps?.options?.length ?? 0) - 1 &&
                  infinite}
              </Fragment>
            )}
            renderInput={params => (
              <TextField
                helperText={fieldState?.error?.message}
                {...merge(params, textFieldProps)} // deep merging to override nested props like sx
                error={showErrorState && !!fieldState?.error?.message}
                InputProps={{
                  ...params.InputProps,
                  endAdornment: (
                    <>
                      {loading ? (
                        <CircularProgress
                          color="inherit"
                          size={20}
                        />
                      ) : null}
                      {endInputAdornment || params.InputProps.endAdornment}
                    </>
                  ),
                }}
              />
            )}
            clearOnBlur={clearOnBlur}
            onBlur={(e: any) => {
              if (resetOnBlur) {
                e.preventDefault();
                return;
              }
              autocompleteProps?.freeSolo &&
                field.onChange(
                  autocompleteProps.multiple
                    ? sanitizeValues([...field.value, e.target.value])
                    : e.target.value,
                );
            }}
            {...autocompleteProps}
            sx={{ ...sx }}
          />
        );
      }}
      name={name}
      control={control}
      rules={rules}
    />
  );
}

export default FormAutocomplete;
