/*!
 * Copyright (c) 2024 PLANKA Software GmbH
 * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
 */

import React, { useCallback, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {
  useMarkdownEditor,
  wysiwygToolbarConfigs,
  MarkdownEditorView,
} from '@gravity-ui/markdown-editor';
/* eslint-disable import/no-unresolved */
import { full as toolbarsPreset } from '@gravity-ui/markdown-editor/_/modules/toolbars/presets';
import { ActionName } from '@gravity-ui/markdown-editor/_/bundle/config/action-names';
/* eslint-enable import/no-unresolved */

import { EditorModes } from '../../../constants/Enums';

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

const removedActionNamesSet = new Set([
  ActionName.checkbox,
  ActionName.file,
  ActionName.filePopup,
  ActionName.tabs,
]);

removedActionNamesSet.forEach((actionName) => {
  delete toolbarsPreset.items[actionName];

  Object.entries(toolbarsPreset.orders).forEach(([orderName, order]) => {
    order.forEach((actions, actionsIndex) => {
      toolbarsPreset.orders[orderName][actionsIndex] = actions.filter(
        (action) => action.id || action !== actionName,
      );
    });
  });
});

const commandMenuActions = wysiwygToolbarConfigs.wCommandMenuConfig.filter(
  (action) => !removedActionNamesSet.has(action.id),
);

export const fileToBase64Data = (file) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
  });

const fileUploadHandler = async (file) => {
  const base64Data = await fileToBase64Data(file);
  return { url: base64Data };
};

const MarkdownEditor = React.forwardRef(
  (
    { defaultValue, defaultMode, isError, onChange, onSubmit, onCancel, onModeChange, ...props },
    ref,
  ) => {
    const wrapperRef = useRef(null);

    const handleWrapperRef = useCallback(
      (element) => {
        wrapperRef.current = element;

        if (typeof ref === 'function') {
          ref(element);
        } else if (ref) {
          ref.current = element; // eslint-disable-line no-param-reassign
        }
      },
      [ref],
    );

    const editor = useMarkdownEditor({
      md: {
        breaks: true,
        linkify: true,
        linkifyTlds: null,
      },
      handlers: {
        uploadFile: fileUploadHandler,
      },
      wysiwygConfig: {
        extensionOptions: {
          commandMenu: {
            actions: commandMenuActions,
          },
        },
        searchPanel: false, // TODO: cancel event does not fire when enabled
      },
      // TODO: remove once both search panels are enabled and locales are synced
      markupConfig: {
        searchPanel: false,
      },
      initial: {
        markup: defaultValue,
        mode: defaultMode,
      },
    });

    useEffect(() => {
      const handleChange = () => {
        onChange(editor.getValue());
      };

      const handleSubmit = () => {
        onSubmit();
      };

      const handleCancel = () => {
        onCancel();
      };

      const handleModeChange = ({ mode: nextMode }) => {
        if (onModeChange) {
          onModeChange(nextMode);
        }
      };

      editor.on('change', handleChange);
      editor.on('submit', handleSubmit);
      editor.on('cancel', handleCancel);
      editor.on('change-editor-mode', handleModeChange);

      return () => {
        editor.off('change', handleChange);
        editor.off('submit', handleSubmit);
        editor.off('cancel', handleCancel);
        editor.off('change-editor-mode', handleModeChange);
      };
    }, [onChange, onSubmit, onCancel, onModeChange, editor]);

    useEffect(() => {
      const { current: wrapperElement } = wrapperRef;

      const handlePaste = (event) => {
        event.stopPropagation();
      };

      wrapperElement.addEventListener('paste', handlePaste);

      return () => {
        wrapperElement.removeEventListener('paste', handlePaste);
      };
    }, []);

    return (
      <div
        {...props} // eslint-disable-line react/jsx-props-no-spreading
        ref={handleWrapperRef}
        className={classNames(styles.wrapper, isError && styles.wrapperError)}
      >
        <MarkdownEditorView
          autofocus
          stickyToolbar
          editor={editor}
          toolbarsPreset={toolbarsPreset}
          className={styles.editor}
        />
      </div>
    );
  },
);

MarkdownEditor.propTypes = {
  defaultValue: PropTypes.string.isRequired,
  defaultMode: PropTypes.oneOf(Object.values(EditorModes)),
  isError: PropTypes.bool,
  onChange: PropTypes.func.isRequired,
  onSubmit: PropTypes.func.isRequired,
  onCancel: PropTypes.func.isRequired,
  onModeChange: PropTypes.func,
};

MarkdownEditor.defaultProps = {
  defaultMode: EditorModes.WYSIWYG,
  isError: false,
  onModeChange: undefined,
};

export default React.memo(MarkdownEditor);
