import Immutable from "immutable";
import {
  EditorState,
  SelectionState,
  CharacterMetadata,
  Modifier,
} from "draft-js";

import createEmptyBlock from "../../utils/internals/createEmptyBlock";
import getSelectedBlocks from "../../utils/internals/getSelectedBlocks";

const CHANGE_TYPES = [
  "change-block-type",
  "remove-range",
  "backspace-character",
  "delete-character",
];

const createCodeBlockPlugin = () => {
  const store = {};

  let prevContentState = null;

  return {
    initialize: pluginFunctions => {
      Object.assign(store, pluginFunctions);
    },

    onChange: editorState => {
      const contentState = editorState.getCurrentContent();
      if (contentState !== prevContentState) {
        prevContentState = contentState;
        const changeType = editorState.getLastChangeType();
        if (CHANGE_TYPES.includes(changeType)) {
          const blockMap = contentState.getBlockMap();
          const lastBlock = blockMap.last();
          if (lastBlock.getType() === "code-block") {
            const block = createEmptyBlock();
            const newContentState = contentState.merge({
              blockMap: blockMap.set(block.getKey(), block),
            });
            const newEditorState = EditorState.set(editorState, {
              currentContent: newContentState,
            });
            return newEditorState;
          }
        }
      }
      return editorState;
    },

    handleKeyCommand: command => {
      if (command !== "code") return "not-handled";

      const editorState = store.getEditorState();
      const selection = editorState.getSelection();
      const contentState = editorState.getCurrentContent();

      const blocks = getSelectedBlocks(contentState, selection);
      const codeBlocks = blocks.filter(
        block => block.getType() === "code-block",
      );

      if (codeBlocks.size) {
        let startBlock = blocks.first();
        let endBlock = blocks.last();

        let newBlockMap = new Immutable.OrderedMap().asMutable();
        let codeBlockQueue = codeBlocks;
        let nextCodeBlock = codeBlockQueue.first();

        contentState.getBlockMap().forEach((block, blockKey) => {
          if (block !== nextCodeBlock) {
            newBlockMap.set(blockKey, block);
            return;
          }

          const code = block.getText();
          const blocks = code.split("\n").map(line => {
            const block = createEmptyBlock().merge({
              text: line,
              characterList: Immutable.Repeat(
                CharacterMetadata.EMPTY,
                line.length,
              ).toList(),
            });
            return block;
          });

          blocks.forEach(block => {
            newBlockMap.set(block.getKey(), block);
          });

          if (block === startBlock) {
            startBlock = blocks[0];
          }
          if (block === endBlock) {
            endBlock = blocks[blocks.length - 1];
          }

          codeBlockQueue = codeBlockQueue.slice(1);
          nextCodeBlock = codeBlockQueue.first();
        });

        const newContentState = contentState.merge({
          blockMap: newBlockMap.asImmutable(),
        });

        const newEditorState = EditorState.forceSelection(
          EditorState.push(editorState, newContentState, "insert-fragment"),
          new SelectionState({
            hasFocus: true,
            anchorKey: startBlock.getKey(),
            anchorOffset: 0,
            focusKey: endBlock.getKey(),
            focusOffset: endBlock.getLength(),
          }),
        );
        store.setEditorState(newEditorState);
      } else {
        const code = blocks.map(block => block.getText()).join("\n");
        const startBlock = blocks.first();
        const endBlock = blocks.last();

        const removeSelection = new SelectionState({
          hasFocus: true,
          anchorKey: startBlock.getKey(),
          anchorOffset: 0,
          focusKey: endBlock.getKey(),
          focusOffset: endBlock.getLength(),
        });

        let newContentState = Modifier.removeRange(
          contentState,
          removeSelection,
          "backward",
        );
        newContentState = Modifier.setBlockType(
          newContentState,
          newContentState.getSelectionAfter(),
          "code-block",
        );
        newContentState = Modifier.insertText(
          newContentState,
          newContentState.getSelectionAfter(),
          code,
        );

        const newEditorState = EditorState.forceSelection(
          EditorState.push(editorState, newContentState, "insert-fragment"),
          newContentState.getSelectionAfter().merge({ anchorOffset: 0 }),
        );
        store.setEditorState(newEditorState);
      }

      return "handled";
    },

    handleReturn: () => {
      const editorState = store.getEditorState();
      const selection = editorState.getSelection();

      const contentState = editorState.getCurrentContent();
      const blockKey = selection.getStartKey();
      const block = contentState.getBlockForKey(blockKey);

      if (block.getType() !== "code-block") return "not-handled";

      let newContentState = Modifier.removeRange(
        contentState,
        selection,
        "backward",
      );
      newContentState = Modifier.insertText(
        newContentState,
        newContentState.getSelectionAfter(),
        "\n",
      );
      const newEditorState = EditorState.push(
        editorState,
        newContentState,
        "insert-fragment",
      );
      store.setEditorState(newEditorState);
      return "handled";
    },
  };
};

export default createCodeBlockPlugin;

@@ -1,12 +1,15 @@
+// 
 import Immut
@@ -56,16 +56,19 @@
 State,%0A 
+ //
  Selecti
@@ -77,16 +77,19 @@
 State,%0A 
+ //
  Charact
@@ -100,16 +100,29 @@
 tadata,%0A
+  RichUtils,%0A
   Modifi
@@ -216,16 +216,19 @@
 Block%22;%0A
+// 
 import g
@@ -1528,32 +1528,268 @@
 n %22not-handled%22;
+%0A      store.setEditorState(%0A        RichUtils.toggleBlockType(store.getEditorState(), %22code-block%22),%0A      );%0A      return %22handled%22;%0A    %7D,%0A%0A    /*%0A    handleKeyCommand: command =%3E %7B%0A      if (command !== %22code%22) return %22not-handled%22;
 %0A%0A      const ed
@@ -5031,32 +5031,39 @@
 handled%22;%0A    %7D,
+%0A    */
 %0A%0A    handleRetu

use alternative convert mode for code block