import type monaco from "monaco-editor"
import styled from "@emotion/styled"
import { memo, useEffect, useRef } from "react"
import { useEvent, useEventListener } from "@aet/hooks"
import { gql, useQuery } from "~/generated/graphql"
import { Monaco } from "./Monaco"
import { EditorTabs } from "./Tabs"
import { NavView } from "./Navigation"
import { LIGHT_MODE, token } from "../theme"
import { editorNavigation } from "../utils/emitter"
import { useFileSaver } from "../hooks/useSaveFile"
import { getActiveFile, useDispatch, useSelector } from "../store"
import { setFileCode } from "../store/actions"
import { showErrorToast } from "~/shared/hooks/useShowApolloError"
import { FileState } from "~/shared/models/enums.generated"

const EDITOR_CONTAINER_QUERY = gql<"EditorContainerQuery">`
  query EditorContainerQuery($id: ID!) {
    file(id: $id) {
      id
      state
    }
    preferences {
      formatOnSave
    }
  }
`

export const Editor = memo(() => {
  const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>()
  const style = useSelector(getActiveFile)
  const dispatch = useDispatch()

  const updateCode = useEvent((code: string) => {
    dispatch(setFileCode(code))
  })

  const { data } = useQuery(EDITOR_CONTAINER_QUERY, {
    variables: { id: style?.id as string },
    skip: !style?.id,
    onError: showErrorToast,
  })

  const save = useFileSaver()

  const isWritable = data?.file?.state === FileState.None
  const formatOnSave = data?.preferences?.formatOnSave ?? false

  useEffect(
    () =>
      editorNavigation.listen(({ fileId: styleId, position }) => {
        const editor = editorRef.current
        if (!editor || !style?.id) return

        if (style.id === styleId || styleId == null) {
          if (typeof position === "number") {
            position = editor.getModel()!.getPositionAt(position)
          }
          editor.setPosition(position)
          editor.revealLine(position.lineNumber)
          editor.focus()
        }
      }),
    [style?.id],
  )

  useEffect(() => {
    editorRef.current?.focus()
  }, [style?.id])

  // Meta + S to save
  useEventListener(window)("keydown", async e => {
    if (e.metaKey && e.key === "s" && style?.id != null) {
      e.preventDefault()
      if (formatOnSave) {
        await editorRef.current!.getAction("editor.action.formatDocument")!.run()
      }
      await new Promise(resolve => setTimeout(resolve, 90))
      await save(style.id, editorRef.current!.getValue())
    }
  })

  // Meta + Shift + P to trigger the command palette in editor
  useEventListener(window)("keydown", e => {
    if (e.metaKey && e.shiftKey && e.key === "p") {
      editorRef.current!.trigger("keyboard", "editor.action.quickCommand", {})
    }
  })

  return (
    <EditorContainer>
      <EditorTabs />
      <NavView />
      <MonacoContainer>
        {style != null && (
          <Monaco
            editorRef={editorRef}
            fileName={`${style.id}.scss`}
            content={style.sections[0].code}
            onContentChange={updateCode}
            readOnly={!isWritable}
          />
        )}
      </MonacoContainer>
    </EditorContainer>
  )
})

const EditorContainer = styled.div`
  display: flex;
  flex-direction: column;
  height: 100%;
  ${LIGHT_MODE} {
    background: ${token.canvas.inset};
  }
`

const MonacoContainer = styled.div`
  flex-grow: 1;
`
