import React, {
  forwardRef,
  ChangeEvent,
  FocusEvent,
  KeyboardEvent,
  useState,
  useMemo,
} from 'react';
import { v4 as uuid } from 'uuid';

// eslint-disable-next-line mui-path-imports/mui-path-imports
import { Autocomplete, Chip, TextField } from '@mui/material';

import { NumericFormat, NumericFormatProps } from 'react-number-format';
import type { MRT_Cell, MRT_TableInstance } from '..';
import { invertColor } from '../column.utils';
import Virtualize from './MRT_EditCellSelectField';
import type { TextFieldProps } from '@mui/material/TextField';

interface Props<TData extends Record<string, any> = {}> {
  cell: MRT_Cell<TData>;
  table: MRT_TableInstance<TData>;
  showLabel?: boolean;
}

interface CustomProps {
  onChange: (event: { target: { name: string; value: string } }) => void;
  inputRef: any;
  name: string;
}

export const MRT_EditCellTextField = <TData extends Record<string, any> = {}>({
  cell,
  showLabel,
  table,
}: Props<TData>) => {
  const {
    getState,
    options: { muiTableBodyCellEditTextFieldProps },
    refs: { editInputRefs },
    setEditingRow,
  } = table;
  const { column, row } = cell;
  const { columnDef } = column;
  const { editingRow } = getState();

  const [value, setValue] = useState(() => {
    if (columnDef.accessorObjectFn) {
      // when it is a select edit, we need to get the value from the accessorObjectFn
      const accessorObject = columnDef.accessorObjectFn(cell.row.original);

      if (Array.isArray(accessorObject)) {
        return accessorObject;
      }

      return columnDef.accessorObjectFn(cell.row.original);
    }

    return cell.getValue<string>();
  });

  const mTableBodyCellEditTextFieldProps =
    muiTableBodyCellEditTextFieldProps instanceof Function
      ? muiTableBodyCellEditTextFieldProps({ cell, column, row, table })
      : muiTableBodyCellEditTextFieldProps;

  const mcTableBodyCellEditTextFieldProps =
    columnDef.muiTableBodyCellEditTextFieldProps instanceof Function
      ? columnDef.muiTableBodyCellEditTextFieldProps({
          cell,
          column,
          row,
          table,
        })
      : columnDef.muiTableBodyCellEditTextFieldProps;

  const textFieldProps: TextFieldProps = {
    ...mTableBodyCellEditTextFieldProps,
    ...mcTableBodyCellEditTextFieldProps,
  };

  const isSelectEdit = columnDef.editVariant === 'select';
  const isTagEdit = columnDef.editVariant === 'tag';
  const isCurrencyEdit = columnDef.editVariant === 'currency';
  const isPercentEdit = columnDef.editVariant === 'percent';

  const saveRow = (newValue: string) => {
    if (editingRow) {
      setEditingRow({
        ...editingRow,
        _valuesCache: { ...editingRow._valuesCache, [column.id]: newValue },
      });
    }
  };

  const handleChange = (event: ChangeEvent<HTMLInputElement> | any) => {
    if (isTagEdit) {
      textFieldProps.onChange?.(event);
      setValue(event);
      // add conditional to make changes when the inpout is out of focus (instead of select the option, because it's a multiple select)
      saveRow(event);
    } else if (isSelectEdit) {
      textFieldProps.onChange?.(event);
      setValue(event.target.value);
      if (textFieldProps?.select) {
        saveRow(event.target.value?.value ?? event.target.value);
      }
    } else if (isCurrencyEdit) {
      textFieldProps.onChange?.(event);
      setValue(event.target.value);
      if (textFieldProps?.select) {
        saveRow(event.target.value);
      }
    } else {
      textFieldProps.onChange?.(event);
      setValue(event.target.value);
      if (textFieldProps?.select) {
        saveRow(event.target.value);
      }
    }
  };

  const handleBlur = (event: FocusEvent<HTMLInputElement>) => {
    if (isTagEdit) {
      textFieldProps.onBlur?.(value);
    } else if (isSelectEdit) {
      textFieldProps.onBlur?.({
        ...event,
        target: {
          ...event.target,
          value: value?.value,
        },
      });
    } else if (isCurrencyEdit) {
      textFieldProps.onBlur?.({
        ...event,
        target: {
          ...event.target,
          value,
        },
      });
    } else {
      textFieldProps.onBlur?.(event);
    }
    saveRow(value);
  };

  const handleEnterKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
    textFieldProps.onKeyDown?.(event);
  };

  if (columnDef.Edit) {
    return <>{columnDef.Edit?.({ cell, column, row, table })}</>;
  }

  if (isTagEdit) {
    const options: any = [];
    if (columnDef.editSelectOptionsFn instanceof Function) {
      options.push(...columnDef.editSelectOptionsFn(cell.row.original));
    } else if (columnDef.editSelectOptions instanceof Array) {
      options.push(...columnDef.editSelectOptions);
    }

    return (
      <Autocomplete
        multiple
        autoSelect
        size="small"
        limitTags={5}
        disableCloseOnSelect
        options={options}
        value={value}
        getOptionLabel={(option) => option?.name}
        renderInput={(params) => (
          <TextField
            {...params}
            size="small"
            variant="standard"
            inputRef={(inputRef) => {
              if (inputRef) {
                editInputRefs.current[column.id] = inputRef;
                if (textFieldProps.inputRef) {
                  textFieldProps.inputRef = inputRef;
                }
              }
            }}
          />
        )}
        onBlur={handleBlur}
        onChange={(_event, value) => handleChange(value)}
        onKeyDown={handleEnterKeyDown}
        autoHighlight
        isOptionEqualToValue={(option, value) => option?.id === value?.id}
        renderTags={(value, getTagProps) => {
          return value.map((option, index) => (
            <Chip
              size="small"
              style={{
                backgroundColor: option?.color ?? '#ffffffff',
                color: invertColor(option?.color ?? '#ffffffff', true),
              }}
              variant="outlined"
              label={option?.name ?? 'undefined'}
              {...getTagProps({ index })}
            />
          ));
        }}
        sx={{ minWidth: '180px', width: '100%' }}
      />
    );
  }

  if (isSelectEdit) {
    // prettier-ignore
    const options: (string | {text: string; value: string | number; label?: string; sub_label?: string; sub_label_variant?: string })[] = [];
    if (columnDef.editSelectOptionsFn instanceof Function) {
      options.push(...columnDef.editSelectOptionsFn(cell.row.original));
    } else if (columnDef.editSelectOptions instanceof Array) {
      options.push(...columnDef.editSelectOptions);
    }

    return (
      <Virtualize
        options={options}
        value={value}
        // prettier-ignore
        getOptionLabel={(option: (string | { text: string; value: string | number; label?: string; sub_label?: string; sub_label_variant?: string })) => {
          if (typeof option === 'object') {
            return option.label ?? option.text ?? option.value?.toString() ?? '';
          }

          return option;
        }}
        {...textFieldProps}
        onInputRef={(inputRef: any) => {
          if (inputRef) {
            editInputRefs.current[column.id] = inputRef;
            if (textFieldProps.inputRef) {
              textFieldProps.inputRef = inputRef;
            }
          }
        }}
        onBlur={handleBlur}
        onChange={(event: ChangeEvent<HTMLInputElement> | any, value: any) => {
          handleChange({
            ...event,
            ...(event?.target && {
              target: {
                ...event.target,
                value,
              },
            }),
          });
        }}
        onKeyDown={handleEnterKeyDown}
      />
    );
  }

  if (isCurrencyEdit) {
    const NumericFormatCustom = forwardRef<NumericFormatProps, CustomProps>(
      (props, ref) => {
        const { onChange, inputRef, ...other } = props;

        return (
          <NumericFormat
            {...other}
            value={value}
            getInputRef={ref}
            onValueChange={(values) => {
              onChange({
                target: {
                  name: props.name,
                  value: values.value,
                },
              });
            }}
            thousandSeparator
            valueIsNumericString
            prefix="$ "
          />
        );
      },
    );

    const textFieldId = useMemo(() => uuid(), []);

    const endAdornment = useMemo(() => {
      return columnDef.getEndAdornment?.(cell.row.original);
    }, [columnDef.getEndAdornment]);

    return (
      <>
        <TextField
          key={textFieldId}
          disabled={
            (columnDef.enableEditing instanceof Function
              ? columnDef.enableEditing(row)
              : columnDef.enableEditing) === false
          }
          autoFocus
          fullWidth
          inputRef={(inputRef) => {
            if (inputRef) {
              editInputRefs.current[column.id] = inputRef;
              if (textFieldProps.inputRef) {
                textFieldProps.inputRef = inputRef;
              }
            }
          }}
          InputProps={{
            inputComponent: NumericFormatCustom as any,
            endAdornment,
          }}
          type={isPercentEdit ? 'number' : 'text'}
          inputProps={{
            ...(isPercentEdit && { step: 0.001 }),
            sx: { padding: '3px 0px', fontSize: 14 },
          }}
          label={showLabel ? column.columnDef.header : undefined}
          margin="none"
          name={column.id}
          placeholder={columnDef.header}
          select={isSelectEdit}
          variant="standard"
          {...textFieldProps}
          onClick={(e) => {
            e.stopPropagation();
            textFieldProps?.onClick?.(e);
          }}
          onBlur={handleBlur}
          onChange={handleChange}
          onKeyDown={handleEnterKeyDown}
        >
          {textFieldProps.children}
        </TextField>
      </>
    );
  }

  return (
    <TextField
      disabled={
        (columnDef.enableEditing instanceof Function
          ? columnDef.enableEditing(row)
          : columnDef.enableEditing) === false
      }
      fullWidth
      inputRef={(inputRef) => {
        if (inputRef) {
          editInputRefs.current[column.id] = inputRef;
          if (textFieldProps.inputRef) {
            textFieldProps.inputRef = inputRef;
          }
        }
      }}
      type={isPercentEdit ? 'number' : 'text'}
      inputProps={{
        ...(isPercentEdit && { step: 0.001 }),
        sx: { padding: '3px 0px', fontSize: 14 },
      }}
      label={showLabel ? column.columnDef.header : undefined}
      margin="none"
      name={column.id}
      placeholder={columnDef.header}
      select={isSelectEdit}
      value={value}
      variant="standard"
      {...textFieldProps}
      onClick={(e) => {
        e.stopPropagation();
        textFieldProps?.onClick?.(e);
      }}
      onBlur={handleBlur}
      onChange={handleChange}
      onKeyDown={handleEnterKeyDown}
    >
      {textFieldProps.children}
    </TextField>
  );
};
