import styled from "@emotion/styled";
import * as React from "react";
import { IoIosPricetag } from "react-icons/io";
import {
  MdFormatBold,
  MdFormatItalic,
  MdFormatListBulleted,
  MdFormatQuote,
  MdFormatUnderlined,
  MdLooks3,
  MdLooksOne,
  MdLooksTwo,
} from "react-icons/md";
import { Editor, Transforms } from "slate";
import { jsx } from "slate-hyperscript";
import { useSlate } from "slate-react";
import { DARK_TERTIARY_ONE, DARK_TERTIARY_THREE } from "../../../shared/colors";
import withPlatform, { PlatformProps } from "../../platform/withPlatform";

const LIST_TYPES = ["numbered-list", "bulleted-list"];

const ELEMENT_TAGS = {
  A: (el: any) => ({ type: "link", url: el.getAttribute("href") }),
  BLOCKQUOTE: () => ({ type: "quote" }),
  H1: () => ({ type: "heading-one" }),
  H2: () => ({ type: "heading-two" }),
  H3: () => ({ type: "heading-three" }),
  H4: () => ({ type: "heading-four" }),
  H5: () => ({ type: "heading-five" }),
  H6: () => ({ type: "heading-six" }),
  IMG: (el: any) => ({ type: "image", url: el.getAttribute("src") }),
  LI: () => ({ type: "list-item" }),
  OL: () => ({ type: "numbered-list" }),
  P: () => ({ type: "paragraph" }),
  PRE: () => ({ type: "code" }),
  UL: () => ({ type: "bulleted-list" }),
};

// COMPAT: `B` is omitted here because Google Docs uses `<b>` in weird ways.
const TEXT_TAGS = {
  CODE: () => ({ code: true }),
  DEL: () => ({ strikethrough: true }),
  EM: () => ({ italic: true }),
  I: () => ({ italic: true }),
  S: () => ({ strikethrough: true }),
  STRONG: () => ({ bold: true }),
  U: () => ({ underline: true }),
};

type Props = {} & PlatformProps;

function EditorToolbar(props: Props) {
  return (
    <Toolbar>
      <MarkButton format="bold" />
      <MarkButton format="italic" />
      <MarkButton format="underline" />
      <MarkButton format="highlight" />
      <BlockButton format="heading-one" />
      <BlockButton format="heading-two" />
      <BlockButton format="heading-three" />
      <BlockButton format="block-quote" />
      <BlockButton format="bulleted-list" />
    </Toolbar>
  );
}

export default withPlatform(EditorToolbar);

const BlockButton = ({ format }: { format: string }) => {
  const editor = useSlate();
  let iconComponent: any = null;
  switch (format) {
    case "heading-one":
      iconComponent = <MdLooksOne style={{ height: "25px", width: "25px" }} />;
      break;
    case "heading-two":
      iconComponent = <MdLooksTwo style={{ height: "25px", width: "25px" }} />;
      break;
    case "heading-three":
      iconComponent = <MdLooks3 style={{ height: "25px", width: "25px" }} />;
      break;
    case "block-quote":
      iconComponent = (
        <MdFormatQuote style={{ height: "25px", width: "25px" }} />
      );
      break;
    case "bulleted-list":
      iconComponent = (
        <MdFormatListBulleted style={{ height: "25px", width: "25px" }} />
      );
      break;
  }

  return (
    <StyleToggler
      active={isBlockActive(format, editor)}
      onMouseDown={(e) => {
        toggleBlock(format, editor);
        e.preventDefault();
      }}
    >
      {iconComponent}
    </StyleToggler>
  );
};

const MarkButton = ({ format }: { format: string }) => {
  const editor = useSlate();
  let iconComponent: any = null;
  switch (format) {
    case "bold":
      iconComponent = (
        <MdFormatBold style={{ height: "25px", width: "25px" }} />
      );
      break;
    case "italic":
      iconComponent = (
        <MdFormatItalic style={{ height: "25px", width: "25px" }} />
      );
      break;
    case "underline":
      iconComponent = (
        <MdFormatUnderlined style={{ height: "25px", width: "25px" }} />
      );
      break;
    case "highlight":
      iconComponent = (
        <IoIosPricetag style={{ height: "25px", width: "25px" }} />
      );
      break;
  }
  return (
    <StyleToggler
      active={isMarkActive(format, editor)}
      onMouseDown={(e) => {
        toggleMark(format, editor);
        e.preventDefault();
      }}
    >
      {iconComponent}
    </StyleToggler>
  );
};

export const isMarkActive = (format: string, editor: Editor) => {
  const marks = Editor.marks(editor);
  return marks ? marks[format] === true : false;
};

export const isBlockActive = (format: string, editor: Editor) => {
  const [match] = Editor.nodes(editor, {
    match: (n) => n.type === format,
  }) as any;

  return !!match;
};

export const toggleBlock = (format: string, editor: Editor) => {
  const isActive = isBlockActive(format, editor);
  const isList = LIST_TYPES.includes(format);

  Transforms.unwrapNodes(editor, {
    match: (n) => LIST_TYPES.includes((n as any).type),
    split: true,
  });

  Transforms.setNodes(editor, {
    type: isActive ? "paragraph" : isList ? "list-item" : format,
  });

  if (!isActive && isList) {
    const block = { type: format, children: [] };
    Transforms.wrapNodes(editor, block);
  }
};

export const toggleMark = (format: string, editor: Editor) => {
  const isActive = isMarkActive(format, editor);
  isActive
    ? Editor.removeMark(editor, format)
    : Editor.addMark(editor, format, true);
};

export const deserialize = (el: any) => {
  // Need the !== \n to block against Notion stuff for some reason
  if (el.nodeType === 3 && el.textContent !== "\n") {
    return el.textContent;
  } else if (el.nodeType !== 1) {
    return null;
  } else if (el.nodeName === "BR") {
    return "\n";
  }

  const { nodeName } = el;
  let parent = el;

  if (
    nodeName === "PRE" &&
    el.childNodes[0] &&
    el.childNodes[0].nodeName === "CODE"
  ) {
    parent = el.childNodes[0];
  }

  const children: any = Array.from(parent.childNodes).map(deserialize).flat();

  if (el.nodeName === "BODY") {
    return jsx("fragment", {}, children);
  }

  if ((ELEMENT_TAGS as any)[nodeName]) {
    const attrs = (ELEMENT_TAGS as any)[nodeName](el);
    return jsx("element", attrs, children);
  }

  if ((TEXT_TAGS as any)[nodeName]) {
    const attrs = (TEXT_TAGS as any)[nodeName](el);
    return children.map((child: any) => jsx("text", attrs, child));
  }

  return children;
};

const Toolbar = styled.div`
  position: relative;
  display: flex;
  justify-content: center;
`;

const StyleToggler = styled.div<{ active: boolean }>`
  cursor: pointer;
  height: 40px;
  width: 40px;
  color: ${(props) => (props.active ? DARK_TERTIARY_THREE : DARK_TERTIARY_ONE)};
`;
