import { useState, useEffect } from 'react';
import './CustomTagSelector.less';
import { useAppDispatch } from '../../store/hooks';
import { clearSelection } from '../../store/uiSlice';
import { TagAttachment, AggregateTagAttachment } from './tag_utils';
import { Menu, MenuItem, Tooltip } from '@mui/joy';

interface CustomTagSelectorProps {
  options: string[];
  attachments: (TagAttachment | AggregateTagAttachment)[];
  onAdd: (value: string) => void;
  onDelete: (value: string) => void;
  onClickTag: (value: string) => void;
  onDoubleClickTag: (value: string) => void;
  multiSelect?: boolean;
  hideAddButton?: boolean;
  proposed?: boolean;
}
export function CustomTagSelector({
  options,
  attachments,
  onAdd,
  onDelete,
  onClickTag,
  onDoubleClickTag,
  multiSelect = false,
  hideAddButton = false,
  proposed = false,
}: CustomTagSelectorProps) {
  const [selected, setSelected] = useState('');
  const dispatch = useAppDispatch();
  const [editMode, setEditMode] = useState(false);
  const [contextMenu, setContextMenu] = useState<{
    top: number;
    left: number;
    tagName: string;
  } | null>(null);

  useEffect(() => {
    if (contextMenu) {
      const handleGlobalClick = () => setContextMenu(null);
      const handleKeyDown = (e: KeyboardEvent) => {
        if (e.key === 'Escape') setContextMenu(null);
      };

      const frameId = requestAnimationFrame(() => {
        window.addEventListener('click', handleGlobalClick);
        window.addEventListener('contextmenu', handleGlobalClick);
        window.addEventListener('keydown', handleKeyDown);
      });

      return () => {
        cancelAnimationFrame(frameId);
        window.removeEventListener('click', handleGlobalClick);
        window.removeEventListener('contextmenu', handleGlobalClick);
        window.removeEventListener('keydown', handleKeyDown);
      };
    }
  }, [contextMenu]);

  const handleContextMenu = (event: React.MouseEvent, tagName: string) => {
    event.preventDefault();
    event.stopPropagation();
    setContextMenu({
      top: event.clientY,
      left: event.clientX,
      tagName: tagName,
    });
  };

  const handleMenuClose = () => {
    setContextMenu(null);
  };

  const selectedPills = attachments.map((attachment) => (
    <TagPill
      attachment={attachment}
      key={attachment.name}
      selected={selected == attachment.name}
      proposed={proposed}
      onClickTag={(s) => {
        if (!multiSelect) {
          dispatch(clearSelection());
        }
        setSelected(s == selected ? '' : s);
        onClickTag(s);
      }}
      onDoubleClickTag={(s) => {
        onDoubleClickTag(s);
      }}
      onDelete={(s) => {
        setSelected('');
        onDelete(s);
      }}
      onContextMenu={(e) => handleContextMenu(e, attachment.name)}
    />
  ));

  return (
    <div className="customTagSelector">
      {selectedPills}
      <Menu
        open={!!contextMenu}
        onClose={handleMenuClose}
        anchorEl={
          contextMenu
            ? {
                getBoundingClientRect: () => ({
                  top: contextMenu.top,
                  left: contextMenu.left,
                  right: contextMenu.left,
                  bottom: contextMenu.top,
                  width: 0,
                  height: 0,
                  toJSON: () => {},
                  x: contextMenu.left,
                  y: contextMenu.top,
                }),
              }
            : null
        }
      >
        <MenuItem
          onClick={() => {
            if (contextMenu) onDoubleClickTag(contextMenu.tagName);
            handleMenuClose();
          }}
        >
          Focus '{contextMenu?.tagName}'
        </MenuItem>
        <MenuItem
          onClick={() => {
            if (contextMenu) onDelete(contextMenu.tagName);
            handleMenuClose();
          }}
          color="danger"
        >
          Remove Tag
        </MenuItem>
      </Menu>
      {editMode ? (
        <>
          <input
            list="tagOptions"
            type="text"
            autoFocus={true}
            onSelect={() => {
              if (!multiSelect) {
                dispatch(clearSelection());
              }
            }}
            onKeyUp={(e) => {
              // clicking a datalist option produces a keydown event of type Event, not KeyboardEvent
              // https://stackoverflow.com/questions/31901351/how-to-fire-an-event-on-clicking-data-list-option-list
              if (!(e.nativeEvent instanceof KeyboardEvent) || e.key === 'Enter') {
                const t = e.target as HTMLInputElement;
                onAdd(t.value);
                t.value = '';
              }
            }}
            onKeyDown={(e) => {
              if (e.key === 'Escape') {
                setEditMode(false);
              }
              if (
                e.key === 'Backspace' &&
                e.target instanceof HTMLInputElement &&
                e.target.value === ''
              ) {
                if (selected == attachments[attachments.length - 1].name) {
                  onDelete(attachments[attachments.length - 1].name);
                } else {
                  setSelected(attachments[attachments.length - 1].name);
                }
              }
            }}
            onBlur={(e) => {
              setEditMode(false);
            }}
          />
          <datalist id="tagOptions">
            {options?.map((option) => (
              <option key={option} value={option} />
            ))}
          </datalist>
        </>
      ) : hideAddButton ? null : (
        <Tooltip title="Add Tag" placement="bottom">
          <div
            className={'tagPillPlus pagerowbutton'}
            tabIndex={0}
            onClick={(e) => {
              setEditMode(true);
            }}
          >
            +
          </div>
        </Tooltip>
      )}
    </div>
  );
}

export function TagPill({
  attachment,
  selected,
  proposed = false,
  onClickTag,
  onDoubleClickTag,
  onDelete,
  onContextMenu,
}: {
  attachment: TagAttachment | AggregateTagAttachment;
  selected: boolean;
  proposed?: boolean;
  onClickTag: (name: string) => void;
  onDoubleClickTag: (name: string) => void;
  onDelete: (name: string) => void;
  onContextMenu: (e: React.MouseEvent) => void;
}) {
  const isAutoTag = 'humanEdit' in attachment ? !attachment.humanEdit : false;

  return (
    <div
      className={'tagPill' + (selected ? ' selected' : '') + (isAutoTag ? ' autotag' : '') + (proposed ? ' proposed' : '')}
      style={{ ['--opacity']: attachment.proportion } as React.CSSProperties}
      tabIndex={0}
      onClick={(e) => {
        onClickTag(attachment.name);
      }}
      onDoubleClick={(e) => {
        onDoubleClickTag(attachment.name);
      }}
      onContextMenu={onContextMenu}
      onKeyDown={(e) => {
        if (e.key === 'Backspace' && selected) {
          onDelete(attachment.name);
        }
      }}
    >
      {attachment.name}
    </div>
  );
}
