import { Cell } from '@tanstack/react-table';
import { SelectMetaData } from '../types';
import { useEffect, useRef, useState } from 'react';
import { customToast } from '../../toast';
import { useTableContext } from '../table-context/table-context-provider';
import { useFocusHandler } from '../focus-handler/focus-handler-provider';
import { CellTypes } from '../constants';
import { replaceSpacesWithHyphens } from '../../_utils/utils';
import CustomSelect from '../../select/customSelect';

interface EditableSelectProps<TData> {
  cell: Cell<TData, unknown>;
  onCancel: () => void;
}

export default function EditableSelect<TData>(props: EditableSelectProps<TData>) {
  const [initialValue, setInitialValue] = useState<string | number>('');
  const [newValue, setNewValue] = useState<string | number>('');

  const selectRef = useRef<HTMLInputElement | HTMLButtonElement>(null);
  const baseRef = useRef<HTMLDivElement>(null);

  const { updateData, tableRef, tableName, doesRowHeaderExist } = useTableContext<TData>(
    props.cell,
  );
  const { isCurrentCellFocused } = useFocusHandler({
    cellType: CellTypes.dataCell,
    columnIndex: props.cell.column.getIndex(),
    groupId: null,
    rowId: props.cell.row.id,
    rowIndex: props.cell.row.index,
  });

  const metaData = props.cell.column.columnDef.meta as SelectMetaData<TData>;

  const cancelAndExit = () => {
    setNewValue(props.cell.getValue() as string | number);
    requestAnimationFrame(() => {
      props.onCancel();
    });
  };

  const saveAndExit = () => {
    const isEmpty = newValue.toString().trim().length === 0;
    // throw error if the value is empty
    if (isEmpty) {
      customToast('Value cannot be empty', 'error');
      cancelAndExit();
      return;
    }

    if (newValue !== initialValue) {
      updateData(newValue);
    }

    requestAnimationFrame(() => {
      props.onCancel();
    });
  };

  useEffect(() => {
    selectRef.current?.focus();
    setInitialValue(props.cell.getValue() as string | number);
    setNewValue(props.cell.getValue() as string | number);
  }, []);

  useEffect(() => {
    if (selectRef.current && selectRef.current.tagName !== 'BUTTON') {
      (selectRef.current as HTMLInputElement)?.select();
    }
  }, [initialValue]);

  useEffect(() => {
    if (isCurrentCellFocused && tableRef.current?.contains(document.activeElement)) {
      selectRef.current?.focus();
    }
  }, [isCurrentCellFocused]);

  useEffect(() => {
    function saveData(e: MouseEvent) {
      if (!baseRef.current?.contains(e.target as Node)) {
        if (newValue !== initialValue) {
          saveAndExit();
        } else {
          cancelAndExit();
        }
      }
    }

    document.addEventListener('mousedown', saveData);

    return () => document.removeEventListener('mousedown', saveData);
  }, [newValue]);

  return (
    <div className="relative w-full">
      <CustomSelect
        ref={selectRef}
        baseRef={baseRef}
        {...(metaData.label
          ? { label: metaData.label(props.cell.row.original) }
          : {
              ariaLabelledBy: `${doesRowHeaderExist ? props.cell.row.id : ''} ${replaceSpacesWithHyphens(tableName)}-${props.cell.column.getIndex()}`,
            })}
        options={metaData.options}
        value={newValue}
        placeholder={metaData.placeholder}
        onChange={(e) => setNewValue(e)}
        classNames={{
          label: 'hidden',
        }}
        onKeyDown={(e, isOpen) => {
          if (e.key === 'Enter' && !isOpen) {
            saveAndExit();
          } else if (e.key === 'Escape' && !isOpen) {
            cancelAndExit();
            e.preventDefault();
          }
        }}
        renderCustomLabel={metaData.renderCustomLabel}
      />
    </div>
  );
}
