import React from "react";
import FocusLock from "react-focus-lock";
import { RemoveScroll } from "react-remove-scroll";
import { Controlled as CodeMirror } from "react-codemirror2";
import { EditorChange, Editor } from "codemirror";
import classNames from "classnames";
import { bem } from "../../utilities/bem";
import { useDisclosure } from "../../hooks";
import { JUSTIFY } from "../../types";
import { Box } from "../Box";
import { Flex } from "../Flex";
import { Fixed } from "../Fixed";
import { Link } from "../Link";
import { Markdown } from "../Markdown";
import { Portal } from "../Portal";
import { Stack } from "../Stack";
import { Button, BUTTON_VARIANT } from "../Button";
import { ICON_TYPE } from "../Icon";
import { Loading } from "../Loading";
import { MarkdownHelp } from "./MarkdownHelp";

const cn = "MarkdownEditorOverlay";

export interface MarkdownEditorProps {
  /**
   * Boolean to determine whether or not the overlay is open or closed
   */
  isOpen?: boolean;
  /**
   * Function to call when the cancel button is clicked; it's up to the client
   * to close the editor onCancel if this behavior is desired
   */
  onCancel?: () => void;
  /**
   * Function to call when the save button is clicked; it's up to the client to
   * close the editor onSave if this behavior is desired
   */
  onSave?: () => void;
  /**
   * Function to close the overlay
   */
  onClose?: () => void;
  /**
   * Function to call when change is made; required when updating value via
   * state (controlled component)
   */
  onChange: (editor: Editor, data: EditorChange, val: string) => void;
  /**
   * The editor value
   */
  value: string;
  /**
   * Automatically focus the editor when it is mounted
   */
  autoFocus?: boolean;
  /**
   * Whether the editor should scroll or wrap for long lines
   */
  lineWrapping?: boolean;
  /**
   * Whether or not to show the transparent black backdrop; it should be
   * disabled when using within a modal.
   */
  showBackdrop?: boolean;
  /**
   * A boolean to indicate that an action is being performed; loader blocks the
   * editor from being used by placing an overlay on top of the editor
   */
  isLoading?: boolean;
}

export const MarkdownEditor = (props: MarkdownEditorProps) => {
  const {
    isOpen = false,
    onCancel,
    onSave,
    onClose,
    onChange: onBeforeChange,
    value,
    autoFocus = true,
    lineWrapping = true,
    showBackdrop = true,
    isLoading = false,
  } = props;

  const options = {
    lineNumbers: true,
    autofocus: autoFocus,
    lineWrapping,
  };

  const {
    isOpen: isHelpOpen,
    onOpen: openHelp,
    onClose: closeHelp,
  } = useDisclosure(false);

  return isOpen ? (
    <Portal>
      <FocusLock returnFocus>
        <RemoveScroll>
          <Fixed className={classNames(bem(cn), showBackdrop && "bg-screen")}>
            <Flex className={bem(cn, { e: "popover" })}>
              <Box className="w-1/2">
                <CodeMirror
                  value={value}
                  options={options}
                  onBeforeChange={onBeforeChange}
                />
              </Box>

              <Box className="w-1/2">
                <Stack
                  className={classNames(bem(cn, { e: "controls" }), "border-b")}
                  justify={JUSTIFY.END}
                >
                  {onCancel && (
                    <Button
                      variant={BUTTON_VARIANT.UTILITY}
                      icon={ICON_TYPE.TIMES}
                      onClick={onCancel}
                      aria-label="Cancel edits and close"
                    />
                  )}
                  {onSave && (
                    <Button
                      variant={BUTTON_VARIANT.UTILITY}
                      icon={ICON_TYPE.CHECK}
                      onClick={onSave}
                      aria-label="Save edits and close"
                    />
                  )}
                </Stack>

                <Box className={bem(cn, { e: "preview" })}>
                  <Markdown skipHtml>{value}</Markdown>
                </Box>

                <Stack
                  className={classNames(bem(cn, { e: "controls" }), "border-t")}
                  justify={JUSTIFY.BETWEEN}
                >
                  <Link as="button" onClick={openHelp}>
                    Markdown help
                  </Link>

                  <Button
                    variant={BUTTON_VARIANT.UTILITY}
                    icon={ICON_TYPE.COMPRESS}
                    onClick={onClose}
                    aria-label="Close editor"
                  />
                </Stack>

                <MarkdownHelp isOpen={isHelpOpen} onClose={closeHelp} />
              </Box>

              {isLoading && (
                <Flex className={bem(cn, { e: "loading" })}>
                  <Loading />
                </Flex>
              )}
            </Flex>
          </Fixed>
        </RemoveScroll>
      </FocusLock>
    </Portal>
  ) : null;
};
