import { COLORS, fsm2 } from "@heydovetail/ui-components";
import { EditorState, Plugin } from "prosemirror-state";
import { Decoration, DecorationSet } from "prosemirror-view";
import { style } from "typestyle-react";
import { el } from "../util/dom";
import { PluginDescriptor } from "../util/PluginDescriptor";

export interface PluginState {
  readonly type: "default";
  readonly text: string;
  readonly isBlank: (editorState: EditorState) => boolean;
}

export interface PlaceholderPluginOptions {
  // Determine if an editor state is blank. If true, the placeholder is rendered.
  isBlank: PluginState["isBlank"];

  // The placeholder text to render.
  text: PluginState["text"];
}

const transition = fsm2.createTransition<PluginState>()({
  default: {
    withOptions: (prev, options: Partial<PlaceholderPluginOptions>) => ({ ...prev, ...options })
  }
});

const { key, getPluginState, setPluginState, getPluginStateOrThrow } = new PluginDescriptor<PluginState>("PlaceholderPlugin");

export { setPluginState, transition };

export class PlaceholderPlugin extends Plugin {
  constructor(options: PlaceholderPluginOptions) {
    // Use a pseudo-element to render the placeholder text.
    const className = style({
      $nest: {
        ".ProseMirror &::before": {
          color: COLORS.i40,
          // Using attr() avoids needing to CSS-string encode the text value
          // (TypeStyle does not do this automatically).
          content: "attr(data-text)",
          cursor: "text",
          position: "absolute",
          zIndex: -1
        }
      }
    });

    super({
      key: key,

      state: {
        init(): PluginState {
          return { type: "default", ...options };
        },
        apply(tr, cur: PluginState): PluginState {
          const nextPluginState = getPluginState(tr);
          return nextPluginState !== null ? nextPluginState : cur;
        }
      },

      props: {
        decorations(state) {
          const { isBlank, text } = getPluginStateOrThrow(state);
          if (isBlank(state)) {
            const placeholder = el("span", className);
            placeholder.setAttribute("data-text", text);
            // Chrome renders the caret in a different position during mouse-down
            // (than after mouseup) for paragraphs that have
            placeholder.innerText = "\u200C";
            return DecorationSet.create(state.doc, [
              Decoration.widget(1 /* inside first empty paragraph */, placeholder, {
                // Performance optimisation
                key: text
              })
            ]);
          }
          return null;
        }
      }
    });
  }
}
