import { type FC, useEffect, useRef } from 'react';
import {
  Controller,
  type UseControllerProps,
  useFormContext,
} from 'react-hook-form';

import { type Slice } from '@tiptap/pm/model';
import { EditorContent, useEditor } from '@tiptap/react';
import i18next from 'i18next';
import Box from '@material-hu/mui/Box';
import { styled } from '@material-hu/mui/styles';

import useHuGoTheme from 'src/hooks/useHuGoTheme';

import useExtensions from './hooks/useExtensions';
import TopBar from './TopBar';
import { type ToolbarOptions } from './types';

const DEFAULT_TOOLBAR_OPTIONS: ToolbarOptions = {
  emoji: true,
  bold: true,
  italic: true,
  underline: true,
  strikethrough: true,
  link: true,
  heading: true,
  bulletList: true,
  orderedList: true,
  code: true,
  blockquote: true,
};

export const StyledContainer = styled(Box, {
  shouldForwardProp: prop =>
    !['isFocused', 'isEmpty', 'isReadOnly', 'disabled'].includes(
      prop as string,
    ),
})<{
  isFocused?: boolean;
  isEmpty?: boolean;
  isReadOnly?: boolean;
  disabled?: boolean;
}>(({ theme, isFocused, isEmpty, isReadOnly, disabled }) => ({
  border: isReadOnly ? 'none' : '1px solid',
  borderRadius: '8px',
  borderColor:
    (isFocused && theme.palette.new.border.neutral.brand) ||
    ((isEmpty || disabled) && theme.palette.new.border.neutral.default) ||
    theme.palette.new.text.neutral.disabled,

  '&:hover': {
    borderColor: theme.palette.primary.main,
  },

  '& .tiptap': {
    ...theme.typography.body1,
    color: theme.palette.new.text.neutral.default,
    fontFamily: theme.typography.fontFamily,
    padding: isReadOnly ? 0 : '10px 12px',

    '& p:first-child /* emotion-disable-server-rendering-unsafe-selector-warning-please-do-not-use-this-the-warning-exists-for-a-reason */, & h1:first-child /* emotion-disable-server-rendering-unsafe-selector-warning-please-do-not-use-this-the-warning-exists-for-a-reason */, & h2:first-child /* emotion-disable-server-rendering-unsafe-selector-warning-please-do-not-use-this-the-warning-exists-for-a-reason */, & h3:first-child /* emotion-disable-server-rendering-unsafe-selector-warning-please-do-not-use-this-the-warning-exists-for-a-reason */, & h4:first-child /* emotion-disable-server-rendering-unsafe-selector-warning-please-do-not-use-this-the-warning-exists-for-a-reason */':
      {
        margin: 0,
      },

    '& p:last-child': {
      marginBottom: 0,
    },

    '& h1': {
      ...theme.typography.h5,
    },

    '& h2': {
      ...theme.typography.h6,
    },

    '& h3': {
      ...theme.typography.body1,
    },

    '& h4': {
      ...theme.typography.body2,
    },

    '& p': {
      margin: '1em 0',
    },

    '&:focus': {
      outline: 'none',
    },

    // Placeholder
    '& p.is-editor-empty::before': {
      color: theme.palette.text.disabled,
      content: 'attr(data-placeholder)',
      height: 0,
      float: 'left',
      pointerEvents: 'none',
    },

    '& ul, ol': {
      padding: '0 1rem',
      margin: '1rem 1rem 1rem .4rem',
      'li p': {
        marginTop: '0.25em',
        marginBottom: '0.25em',
      },
    },

    '& code': {
      backgroundColor: theme.palette.background.default,
      borderRadius: '.25rem',
      fontFamily: 'JetBrainsMono, monospace',
      margin: '1.5rem 0',
      padding: '0.25em 0.3em',
      fontSize: '0.8rem',
    },

    '& blockquote': {
      position: 'relative',
      padding: '0 1rem',
      margin: '.75rem 0',
      marginLeft: '1rem',
      '&:before': {
        content: '""',
        position: 'absolute',
        left: 0,
        top: '-0.5rem',
        bottom: '-0.5rem',
        width: '4px',
        backgroundColor: theme.palette.action.disabled,
        borderRadius: '8px',
      },
    },

    '& .mention': {
      color: theme.palette.new.text.neutral.brand,
      backgroundColor: theme.palette.new.background.layout.brand,
      textDecoration: 'underline',
    },
  },
}));

type RichTextEditorProps = Pick<
  UseControllerProps,
  'control' | 'name' | 'rules'
> & {
  content: string;
  placeholder?: string;
  isEdit?: boolean;
  handlePaste?: (
    e: React.ClipboardEvent<HTMLInputElement>,
    slice: Slice,
  ) => void;
  disabled?: boolean;
  toolbarOptions?: ToolbarOptions;
};

const RichTextEditor: FC<RichTextEditorProps> = props => {
  const {
    content,
    placeholder = i18next.t('post:feed_create_post'),
    isEdit,
    handlePaste,
    disabled,
    toolbarOptions,
    ...controllerProps
  } = props;
  const HugoThemeProvider = useHuGoTheme();

  const extensions = useExtensions({
    placeholder: placeholder,
    handlePaste,
  });

  const editor = useEditor({
    extensions,
    content,
    editable: !disabled,
  });

  const form = useFormContext();

  // Update editable state when disabled changes.
  useEffect(() => {
    if (editor) {
      editor.setEditable(!disabled);
    }
  }, [disabled, editor]);

  // Sync external initial content (drafts, edit mode defaults) when it changes.
  useEffect(() => {
    if (!editor || disabled) {
      return;
    }

    const nextContent = content || '';
    const currentContent = editor.isEmpty ? '' : editor.getHTML();
    if (nextContent === currentContent) {
      return;
    }

    editor.commands.setContent(nextContent);
  }, [content, disabled, editor]);

  // For update placeholder after language change
  useEffect(() => {
    if (editor !== null && placeholder !== '') {
      editor.extensionManager.extensions.filter(
        extension => extension.name === 'placeholder',
      )[0].options.placeholder = placeholder;
      editor.view.dispatch(editor.state.tr);
    }
  }, [editor, placeholder]);

  // Clear only after a dirty -> clean transition (e.g. after successful submit/reset).
  const wasDirtyRef = useRef(form.formState.isDirty);

  useEffect(() => {
    const isDirty = form.formState.isDirty;
    const justWentClean = wasDirtyRef.current && !isDirty;
    const canClear = editor && !isEdit && !editor.isEmpty;

    if (justWentClean && canClear) {
      if (!disabled) {
        editor!.commands.setContent('');
        wasDirtyRef.current = isDirty; // consume the transition
      } else {
        // Defer consuming the transition until re-enabled.
        // Keep wasDirtyRef as true so we can retry clearing later.
      }
    } else {
      wasDirtyRef.current = isDirty;
    }

    return () => {
      editor?.off('update', form.reset);
    };
  }, [form.formState.isDirty, editor, isEdit, disabled]);

  return (
    <HugoThemeProvider>
      <Controller
        name={controllerProps.name}
        control={controllerProps.control}
        rules={controllerProps.rules}
        render={({ field: { onChange } }) => {
          if (editor) {
            editor.off('update'); // Prevent duplicate update events
            editor.on('update', () => {
              const newContent = editor.isEmpty ? '' : editor.getHTML();
              onChange(newContent);
            });
          }

          return (
            <StyledContainer
              isEmpty={editor?.isEmpty}
              isFocused={editor?.isFocused}
            >
              {!disabled && editor && (
                <TopBar
                  editor={editor}
                  toolbarOptions={{
                    ...DEFAULT_TOOLBAR_OPTIONS,
                    ...toolbarOptions,
                  }}
                />
              )}
              <EditorContent editor={editor} />
            </StyledContainer>
          );
        }}
      />
    </HugoThemeProvider>
  );
};

export default RichTextEditor;
