import {
  AdmonitionDirectiveDescriptor,
  BlockTypeSelect,
  BoldItalicUnderlineToggles,
  ChangeAdmonitionType,
  ChangeCodeMirrorLanguage,
  codeBlockPlugin,
  codeMirrorPlugin,
  CodeToggle,
  ConditionalContents,
  CreateLink,
  diffSourcePlugin,
  DiffSourceToggleWrapper,
  DirectiveNode,
  directivesPlugin,
  EditorInFocus,
  frontmatterPlugin,
  headingsPlugin,
  imagePlugin,
  InsertAdmonition,
  InsertCodeBlock,
  InsertFrontmatter,
  InsertImage,
  InsertTable,
  InsertThematicBreak,
  linkDialogPlugin,
  linkPlugin,
  listsPlugin,
  ListsToggle,
  markdownShortcutPlugin,
  MDXEditor,
  quotePlugin,
  Separator,
  ShowSandpackInfo,
  StrikeThroughSupSubToggles,
  tablePlugin,
  thematicBreakPlugin,
  toolbarPlugin,
  UndoRedo,
} from "@mdxeditor/editor";
import "@mdxeditor/editor/style.css";
import { Box } from "@mui/material";
import i18next from "i18next";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import "./editor.css";

export interface MarkdownEditorProps {
  value: string;
  darkMode?: boolean;
  initialValue: string;
  onChange: (value: string) => void;
  readOnly?: boolean;
}

function whenInAdmonition(editorInFocus: EditorInFocus | null) {
  const node = editorInFocus?.rootNode;
  if (!node || node.getType() !== "directive") {
    return false;
  }

  return ["note", "tip", "danger", "info", "caution"].includes((node as DirectiveNode).getMdastNode().name);
}

function setEndOfContenteditable(contentEditableElement: HTMLElement) {
  let range: Range | null;
  let selection: Selection | null;
  if (document.createRange) {
    range = document.createRange(); //Create a range (a range is a like the selection but invisible)
    range.selectNodeContents(contentEditableElement); //Select the entire contents of the element with the range
    range.collapse(false); //collapse the range to the end point. false means collapse to end rather than the start
    selection = window.getSelection(); //get the selection object (allows you to change selection)
    selection?.removeAllRanges(); //remove any selections already made
    selection?.addRange(range); //make the range you have just created the visible selection
  }
}

const MarkdownEditor = (props: MarkdownEditorProps) => {
  const { t } = useTranslation();
  const [nsLoaded, setNsLoaded] = useState(false);
  useEffect(() => {
    i18next.loadNamespaces(["markdown_editor"]).then(() => {
      setNsLoaded(true);
    });
  }, []);
  return (
    <Box
      sx={{
        display: "flex",
        flexDirection: "column",
        minHeight: "calc(100vh - 200px)",
      }}
    >
      {nsLoaded && (
        <MDXEditor
          className={props.darkMode ? "dark-theme dark-editor" : undefined}
          translation={(key, _defaultValue, interpolations) => {
            return t("markdown_editor:" + key, interpolations);
          }}
          readOnly={props.readOnly}
          onChange={props.onChange}
          plugins={[
            diffSourcePlugin({
              diffMarkdown: props.initialValue,
              viewMode: "rich-text",
            }),
            toolbarPlugin({
              toolbarContents: () => (
                <ConditionalContents
                  options={[
                    {
                      when: (editor) => editor?.editorType === "codeblock",
                      contents: () => <ChangeCodeMirrorLanguage />,
                    },
                    {
                      when: (editor) => editor?.editorType === "sandpack",
                      contents: () => <ShowSandpackInfo />,
                    },
                    {
                      fallback: () => (
                        <DiffSourceToggleWrapper>
                          <UndoRedo />
                          <Separator />
                          <BoldItalicUnderlineToggles />
                          <CodeToggle />
                          <Separator />
                          <StrikeThroughSupSubToggles />
                          <Separator />
                          <ListsToggle />
                          <Separator />

                          <ConditionalContents
                            options={[
                              {
                                when: whenInAdmonition,
                                contents: () => <ChangeAdmonitionType />,
                              },
                              { fallback: () => <BlockTypeSelect /> },
                            ]}
                          />

                          <Separator />

                          <CreateLink />
                          <InsertImage />

                          <Separator />

                          <InsertTable />
                          <InsertThematicBreak />

                          <Separator />
                          <InsertCodeBlock />

                          <ConditionalContents
                            options={[
                              {
                                when: (editorInFocus) => !whenInAdmonition(editorInFocus),
                                contents: () => (
                                  <>
                                    <Separator />
                                    <InsertAdmonition />
                                  </>
                                ),
                              },
                            ]}
                          />

                          <Separator />
                          <InsertFrontmatter />
                        </DiffSourceToggleWrapper>
                      ),
                    },
                  ]}
                />
              ),
            }),
            listsPlugin(),
            quotePlugin(),
            headingsPlugin({ allowedHeadingLevels: [1, 2, 3] }),
            linkPlugin(),
            linkDialogPlugin(),
            imagePlugin({}),
            tablePlugin(),
            thematicBreakPlugin(),
            frontmatterPlugin(),
            codeBlockPlugin({ defaultCodeBlockLanguage: "" }),
            codeMirrorPlugin({
              codeBlockLanguages: {
                js: "JavaScript",
                jsx: "JSX",
                css: "CSS",
                txt: "Plain Text",
                tsx: "TSX",
                ts: "TypeScript",
                html: "HTML",
                json: "JSON",
                sh: "Shell",
                bash: "Bash",
                yaml: "YAML",
                markdown: "Markdown",
                dockerfile: "Dockerfile",
                sql: "SQL",
                python: "Python",
                go: "Go",
                java: "Java",
                c: "C",
                cpp: "C++",
                php: "PHP",
                ruby: "Ruby",
                perl: "Perl",
                swift: "Swift",
                r: "R",
                rust: "Rust",
                kotlin: "Kotlin",
                scala: "Scala",
                "": "Unspecified",
              },
            }),
            directivesPlugin({
              directiveDescriptors: [AdmonitionDirectiveDescriptor],
            }),
            markdownShortcutPlugin(),
          ]}
          contentEditableClassName={props.darkMode ? "markdown-body-dark" : "markdown-body-light"}
          markdown={props.value}
        />
      )}
      {!nsLoaded && <div className={"mdxeditor"}></div>}
      <Box
        onClick={() => {
          setEndOfContenteditable(
            document.querySelector(props.darkMode ? ".markdown-body-dark" : ".markdown-body-light") as HTMLElement,
          );
        }}
        sx={{
          cursor: "text",
          flexGrow: 1,
        }}
      />
    </Box>
  );
};

export default MarkdownEditor;
