import type { Cell } from '@tanstack/react-table';
import { Input } from '../../input';
import type { EditableTableColumnDef, TextInputMetaData } 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';

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

export default function EditableTextInput<TData>(props: EditableTextInputProps<TData>) {
  const [value, setValue] = useState('');
  const [initialValue, setinitialValue] = useState('');

  const inputRef = useRef<HTMLInputElement>(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 columnMeta = (props.cell.column.columnDef as EditableTableColumnDef<TData>)
    .meta as TextInputMetaData<TData>;

  const cancelAndExit = () => {
    setValue(typeof props.cell.getValue() === 'string' ? (props.cell.getValue() as string) : '');
    requestAnimationFrame(() => {
      props.onCancel();
    });
  };

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

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

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

  const onKeyDown = (e: React.KeyboardEvent) => {
    const tableWrapper = tableRef.current;
    const activeElement = document.activeElement;

    // If we're in an input that's not part of our table, ignore all keydowns
    if (
      (activeElement &&
        activeElement.tagName === 'INPUT' &&
        !tableWrapper?.contains(activeElement)) ||
      e.key === 'Tab'
    ) {
      return;
    }

    if (e.key === 'Enter') {
      saveAndExit();
      e.preventDefault();
    } else if (e.key === 'Escape') {
      cancelAndExit();
      e.preventDefault();
    }
  };

  useEffect(() => {
    inputRef.current?.focus();
    setinitialValue(props.cell.getValue() as string);
    setValue(props.cell.getValue() as string);
  }, []);

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

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

    document.addEventListener('mousedown', saveData);

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

  return (
    <Input
      className={`max-w-[${props.cell.column.columnDef.size}px]`}
      type={columnMeta.type}
      {...(columnMeta.label
        ? { label: columnMeta.label(props.cell.row.original) }
        : {
            'aria-labelledby': `${doesRowHeaderExist ? props.cell.row.id : ''} ${replaceSpacesWithHyphens(tableName)}-${props.cell.column.getIndex()}`,
          })}
      {...(columnMeta.type === 'number' ? { step: columnMeta?.step ?? 0.25 } : {})}
      ref={inputRef}
      value={value}
      onChange={(e: any) => setValue(e.target.value)}
      onKeyDown={onKeyDown}
    />
  );
}
