import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'clsx';
import { closestCenter, DndContext, KeyboardSensor, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { useTheme } from '@mui/material/styles';
import {
  AddOutlined,
  ArrowDropDownCircleRounded,
  CalendarMonthRounded,
  CheckBoxRounded,
  CloseRounded,
  DeleteRounded,
  DragIndicatorRounded,
  EmailRounded,
  FormatColorTextRounded,
  RadioButtonCheckedRounded,
  ViewHeadlineRounded,
} from '@mui/icons-material';

import {
  FIELD_CHECKBOX,
  FIELD_DATE,
  FIELD_EMAIL,
  FIELD_RADIO,
  FIELD_SELECT,
  FIELD_TEXT,
  FIELD_TEXTAREA,
  FIELDS_OPTIONS,
} from '@/modules/forms/editor/constants';

import { getInputPropsByName } from '@/modules/@common/Form/helpers';
import { createDefaultField, createDynamicField, createOption } from '@/modules/forms/helpers/createDynamicField';

import SortableItem from '@/modules/@common/dnd/SortableItem/SortableItem';
import { FormGroup, FormRow, useFormSettings } from '@/modules/@common/Form';
import {
  Button,
  Checkbox,
  Chip,
  FormControlLabel,
  IconButton,
  InputAdornment,
  MenuItem,
  Paper,
  Radio,
  Stack,
  Switch,
  TextField,
  Typography,
} from '@/mui/components';

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

const ICON_MAP = {
  [FIELD_TEXT]: FormatColorTextRounded,
  [FIELD_TEXTAREA]: ViewHeadlineRounded,
  [FIELD_EMAIL]: EmailRounded,
  [FIELD_DATE]: CalendarMonthRounded,
  [FIELD_SELECT]: ArrowDropDownCircleRounded,
  [FIELD_RADIO]: RadioButtonCheckedRounded,
  [FIELD_CHECKBOX]: CheckBoxRounded,
};

const OPTION_MAX_LENGTH = 40;

function DynamicFields({ disabled = false, ...props }) {
  const theme = useTheme();
  const { size } = useFormSettings();
  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    }),
  );

  // fields handlers
  const handleDragEnd = ({ active, over }) => {
    const getIndex = (neededIndex) => props.fields.findIndex((o) => o.id === neededIndex);
    const oldIndex = getIndex(active.id);
    const newIndex = getIndex(over.id);
    const dynamicFields = arrayMove(props.fields, oldIndex, newIndex);

    props.onSetField({ dynamicFields });
  };

  // need discuss about this behavior
  // const handleSetActive = (fieldId, value) => {
  //   const dynamicFields = props.fields.map((field) => ({
  //     ...field,
  //     isSelected: field.id === fieldId ? value : false,
  //   }));
  //
  //   return props.onSetField({ dynamicFields });
  // };

  const handleAdd = () => {
    const dynamicFields = [].concat(props.fields, createDefaultField());
    return props.onSetField({ dynamicFields });
  };

  const handleRemove = (fieldId) => {
    const dynamicFields = props.fields.filter((field) => field.id !== fieldId);
    return props.onSetField({ dynamicFields });
  };

  const handleFieldSetValue = (fieldId, keyOfValue, value) => {
    const dynamicFields = props.fields.map((field) => ({
      ...field,
      [keyOfValue]: field.id === fieldId ? value : field[keyOfValue],
    }));

    return props.onSetField({ dynamicFields });
  };

  const handleChangeComponentType = (fieldId, componentType) => {
    const newComponent = createDynamicField(componentType);

    const dynamicFields = props.fields.map((field) => {
      if (field.id === fieldId) {
        return {
          ...newComponent,
          id: field.id,
          isSelected: field.isSelected,
        };
      }

      return field;
    });

    return props.onSetField({ dynamicFields });
  };

  // fields options handlers
  const updateFieldOptions = (fieldId, options) => {
    const dynamicFields = props.fields.map((field) => {
      if (field.id === fieldId) {
        return { ...field, options };
      }
      return field;
    });

    return props.onSetField({ dynamicFields });
  };

  const handleAddOption = (fieldId, currentOptions) => {
    const maxId = Math.max(...currentOptions.map((o) => o.id));
    const options = [].concat(currentOptions, createOption(maxId + 1));

    updateFieldOptions(fieldId, options);
  };

  const handleSetEditModeOption = (fieldId, optionId, currentOptions) => {
    const options = currentOptions.map((option) => ({
      ...option,
      isEditable: option.id === optionId,
    }));

    updateFieldOptions(fieldId, options);
  };

  const handleSetCloseEditModeOption = (fieldId, optionId, currentOptions) => {
    const options = currentOptions.map((option) => ({
      ...option,
      isEditable: option.id === optionId ? false : option.isEditable,
    }));

    updateFieldOptions(fieldId, options);
  };

  const handleRemoveOption = (fieldId, optionId, currentOptions) => {
    const options = currentOptions.filter((option) => option.id !== optionId);

    updateFieldOptions(fieldId, options);
  };

  const handleSetOptionValue = (fieldId, optionId, value, currentOption) => {
    const options = currentOption.map((option) => ({
      ...option,
      name: option.id === optionId ? value : option.name,
    }));

    updateFieldOptions(fieldId, options);
  };

  const handleSetDefaultOption = (field, optionId, value) => {
    const { id: fieldId, options: currentOptions, component } = field;
    const getOptionValue = (optionIsDefaultValue) => {
      if (component === FIELD_RADIO) {
        return value === optionIsDefaultValue ? false : value;
      }
      return value;
    };
    const getDefaultOptionValue = (optionIsDefaultValue) =>
      component === FIELD_CHECKBOX ? optionIsDefaultValue : false;
    const options = currentOptions.map((option) => ({
      ...option,
      isDefault: option.id === optionId ? getOptionValue(option.isDefault) : getDefaultOptionValue(option.isDefault),
    }));

    updateFieldOptions(fieldId, options);
  };

  return (
    <FormGroup>
      <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
        <SortableContext items={props.fields.map((field) => field.id)} strategy={verticalListSortingStrategy}>
          {props.fields.map((field) => (
            <SortableItem key={field.id} value={field.id} isDragDisabled={disabled}>
              {(sortableItemProps) => (
                <FormRow fullWidth key={field.id} multiline>
                  <Paper
                    key={field.id}
                    elevation={sortableItemProps.dndIsDragging ? 10 : 1}
                    ref={sortableItemProps.dndSetNodeRef}
                    style={sortableItemProps.dndStyle}
                    {...sortableItemProps.dndAttributes}
                    className={classNames(styles.Field, {
                      [styles.Field___selected]: field.isSelected,
                      [styles.Field___dragging]: sortableItemProps.dndIsDragging,
                    })}
                  >
                    <Stack spacing={2} direction="row" alignItems="flex-start" fullWidth>
                      {props.fields.length > 1 && (
                        <IconButton
                          size={size}
                          component="span"
                          className={styles.DynamicAction}
                          {...sortableItemProps.dndListeners}
                        >
                          <DragIndicatorRounded />
                        </IconButton>
                      )}
                      <TextField
                        size={size}
                        value={field.value}
                        placeholder={field.placeholder}
                        disabled={disabled}
                        inputProps={{
                          maxLength: OPTION_MAX_LENGTH,
                        }}
                        InputProps={{
                          endAdornment: (
                            <InputAdornment position="end">
                              <Chip label={`${field.value.length} / ${OPTION_MAX_LENGTH}`} variant="text" size={size} />
                            </InputAdornment>
                          ),
                        }}
                        onChange={({ target }) => handleFieldSetValue(field.id, 'value', target.value)}
                        // eslint-disable-next-line react/prop-types
                        {...getInputPropsByName(props.scheme, ['dynamicFields', field.id, 'value'])}
                        // eslint-disable-next-line react/prop-types
                        onFocus={() => props.removeErrorByPropAction(['dynamicFields', field.id, 'value'])}
                      />
                      <TextField
                        size={size}
                        select
                        fullWidth
                        value={field.component}
                        disabled={disabled || field.isTypeDisabled}
                        onChange={({ target }) => handleChangeComponentType(field.id, target.value)}
                      >
                        {FIELDS_OPTIONS.map((option) => {
                          const Icon = ICON_MAP[option.id];

                          return (
                            <MenuItem value={option.id} key={option.id}>
                              <Stack direction="row" spacing={1} alignItems="center" className={styles.MenuElement}>
                                <Icon htmlColor="info" fontSize="xSmall" />
                                <Typography variant="body2" color="info">
                                  {option.name}
                                </Typography>
                              </Stack>
                            </MenuItem>
                          );
                        })}
                      </TextField>
                      <FormControlLabel
                        size={size}
                        control={
                          <Switch
                            size={size}
                            name={field.id}
                            color="primary"
                            checked={field.isRequire}
                            disabled={disabled}
                            onChange={({ target }) => handleFieldSetValue(field.id, 'isRequire', target.checked)}
                          />
                        }
                        labelPlacement="start"
                        label="Required"
                      />
                      {props.fields.length > 1 && (
                        <IconButton
                          size={size}
                          component="span"
                          disabled={disabled}
                          onClick={() => handleRemove(field.id)}
                          className={styles.DynamicAction}
                        >
                          <DeleteRounded />
                        </IconButton>
                      )}
                    </Stack>
                    <Stack fullWidth />
                    {field?.options?.length && (
                      <Stack spacing={2} className={styles.SettingsList}>
                        {field.options.map((option, fieldIndex) => {
                          const setDefaultControlProps = {
                            color: 'primary',
                            checked: option.isDefault,
                            onClick: ({ target }) => handleSetDefaultOption(field, option.id, target.checked),
                          };
                          const Control = {
                            [FIELD_SELECT]: (
                              <IconButton readOnly size={size}>
                                <Typography variant="body2" color="info">
                                  {fieldIndex + 1}
                                </Typography>
                              </IconButton>
                            ),
                            [FIELD_CHECKBOX]: <Checkbox {...setDefaultControlProps} disabled={disabled} size={size} />,
                            [FIELD_RADIO]: <Radio {...setDefaultControlProps} disabled={disabled} size={size} />,
                          }[field.component];

                          return (
                            <div
                              key={option.id}
                              className={classNames(styles.SettingsWrapper, {
                                // eslint-disable-next-line react/prop-types
                                [styles.SettingsWrapper___active]: props.isEditable,
                              })}
                            >
                              <Stack spacing={2} direction="row" alignItems="flex-start">
                                {Control}
                                <TextField
                                  size={size}
                                  value={option.name}
                                  placeholder={option.placeholder}
                                  autoFocus={fieldIndex >= field.options.length - 1}
                                  disabled={disabled}
                                  onChange={({ target }) =>
                                    handleSetOptionValue(field.id, option.id, target.value, field.options)
                                  }
                                  onBlur={() => handleSetCloseEditModeOption(field.id, option.id, field.options)}
                                  style={{
                                    maxWidth: `calc(${OPTION_MAX_LENGTH}ch + ${theme.spacing(13)})`,
                                  }}
                                  inputProps={{
                                    maxLength: OPTION_MAX_LENGTH,
                                    readOnly: !option.isEditable,
                                  }}
                                  InputProps={{
                                    endAdornment: (
                                      <InputAdornment position="end">
                                        <Chip
                                          size={size}
                                          label={`${option.name.length} / ${OPTION_MAX_LENGTH}`}
                                          variant="text"
                                        />
                                      </InputAdornment>
                                    ),
                                  }}
                                  // eslint-disable-next-line react/prop-types
                                  {...getInputPropsByName(props.scheme, [
                                    'dynamicFields',
                                    field.id,
                                    'options',
                                    option.id,
                                    'name',
                                  ])}
                                  label=""
                                  onFocus={() => {
                                    // eslint-disable-next-line react/prop-types
                                    props.removeErrorByPropAction([
                                      'dynamicFields',
                                      field.id,
                                      'options',
                                      option.id,
                                      'name',
                                    ]);

                                    return handleSetEditModeOption(field.id, option.id, field.options);
                                  }}
                                />
                                {field.options.length > 1 && (
                                  <IconButton
                                    size={size}
                                    className={styles.FieldDelete}
                                    disabled={disabled}
                                    onClick={() => handleRemoveOption(field.id, option.id, field.options)}
                                  >
                                    <CloseRounded />
                                  </IconButton>
                                )}
                              </Stack>
                            </div>
                          );
                        })}
                        <div>
                          <Button
                            variant="text"
                            size={size}
                            color="neutral"
                            startIcon={<AddOutlined />}
                            disabled={disabled}
                            onClick={() => handleAddOption(field.id, field.options)}
                          >
                            Add Option
                          </Button>
                        </div>
                      </Stack>
                    )}
                  </Paper>
                </FormRow>
              )}
            </SortableItem>
          ))}
        </SortableContext>
      </DndContext>
      <FormRow justifyContent="flex-end" fullWidth>
        <Button
          size={size}
          variant="text"
          color="primary"
          label="ADD OPTION"
          disabled={disabled}
          startIcon={<AddOutlined />}
          onClick={() => handleAdd(createDefaultField())}
        >
          Add Field
        </Button>
      </FormRow>
    </FormGroup>
  );
}

DynamicFields.propTypes = {
  fields: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
  onSetField: PropTypes.func.isRequired,
  disabled: PropTypes.bool,
};

export default DynamicFields;
