import { openSearchPanel } from '@codemirror/search'
import { curSql, getCurDatabase, getCurStatements } from '@tidbcloud/codemirror-extension-cur-sql'
import { curSqlGutter } from '@tidbcloud/codemirror-extension-cur-sql-gutter'
import { onDocChange, onSelectionChange } from '@tidbcloud/codemirror-extension-events'
import { fullWidthCharLinter, useDbLinter } from '@tidbcloud/codemirror-extension-linters'
import { saveHelper } from '@tidbcloud/codemirror-extension-save-helper'
import { sqlAutoCompletion } from '@tidbcloud/codemirror-extension-sql-autocomplete'
import { sqlParser, SqlStatement } from '@tidbcloud/codemirror-extension-sql-parser'
import { ActionIcon, Box, Flex, HoverCard, Menu, notifier, Typography } from '@tidbcloud/uikit'
import { useClickOutside, usePrevious } from '@tidbcloud/uikit/hooks'
import {
  IconAiExplore01,
  IconDatabase01,
  IconDatabaseNone,
  IconDotsHorizontal,
  IconInfoCircle
} from '@tidbcloud/uikit/icons'
import { EditorView } from 'codemirror'
import { isNil } from 'lodash-es'
import { useEffect, useMemo, useRef, useState } from 'react'
import { format } from 'sql-formatter'

import { useHelpCenterContext } from 'dbaas/screens/HelpCenter/context'
import { createSqlEditorFile, updateSqlEditorFile } from 'dbaas/services'
import auth from 'dbaas/services/auth'
import { dbaasUrl } from 'dbaas/services/endpoint'
import { getErrorMessage, NetworkErrorMsg } from 'dbaas/services/errorCodes'
import { eventTracking } from 'dbaas/utils/tracking'

import { aiLog, fileSessionMap, getSessionDb, initSessionId, runStatement } from '../api/dbaas'
import { AutoSaveState } from '../AutoSaveState'
import { useChatContext } from '../context/chat-context'
import { useSQLEditorContext } from '../context/editor-context'
import SQLCodeEditor from '../sql-code-editor'
import { addEditorSaveState, clearEditorSaveState } from '../sql-code-editor/editor-auto-save-cache'
import { getEditor } from '../sql-code-editor/editor-cache'
import { aiAssistant } from '../sql-code-editor/extensions/ai-assistant'
import { isUnifiedMergeViewActive } from '../sql-code-editor/extensions/ai-assistant/prompt-input'
import { hideTooltip } from '../sql-code-editor/extensions/ai-assistant/tooltip-hint'
import { getAiGeneratedSqlField } from '../sql-code-editor/extensions/ai-generated-sql'
import { useEditorTheme } from '../sql-code-editor/extensions/editor-theme'
import { sqlConfigConverter } from '../sql-code-editor/extensions/lang-sql'
import { runHotkeys } from '../sql-code-editor/extensions/run-hotkeys'
import { selectionBgHelper } from '../sql-code-editor/extensions/selection-bg'
import { useNewFileContent } from '../SqlFiles/fileContent'
import Tabs from '../Tabs'
import { AI_CODE, AutoSaveStateEnum, RequestStatus, SqlFile } from '../types'

import AIAggModal from './AIAggModal'
import Chat2DataModal from './Chat2DataModal'
import HotKeys from './HotKeys'
import { useStyles } from './index.styles'
import RunBtn from './RunBtn'
import Settings from './Settings'
import TipsBeforeRouterChange from './TipsBeforeRouterChange'

const Editor = (props: { onRun: () => void }) => {
  const [isRunning, setIsRunning] = useState(false)
  const [isExplaining, setIsExplaining] = useState(false)

  const { classes } = useStyles()
  const theme = useEditorTheme()

  const {
    orgId,
    projectId,
    clusterId,

    editorSetting,
    editorSettingLoading,

    dbList,

    getSqlFiles,
    sqlFiles,
    editedSqlFile,
    setEditedSqlFile,
    editedSqlFileRef,
    isCreatingFile,
    setIsCreatingFile,

    setSqlResultList,

    setIsDbNeedFresh,

    setSideMenuActiveIndex,
    layout: { mainPanel },
    refetchAiSetting,
    setAutoSaveState,
    isRunAll,
    setIsRunAll
  } = useSQLEditorContext()

  const [curDb, setCurDb] = useState('')
  const [sessionDb, setSessionDb] = useState('')
  const editedSqlFilePrev = usePrevious(editedSqlFile)

  const changeHandler = useRef<(sql: string) => void>()
  changeHandler.current = (sql: string) => {
    const file = {
      ...editedSqlFileRef.current,
      content: sql,
      has_modified: true
    }
    setEditedSqlFile(file)
    editedSqlFileRef.current = file
  }

  const focusChangeHandler = useRef<(curSql: SqlStatement[]) => void>()
  focusChangeHandler.current = (curSql: SqlStatement[]) => {
    setCurDb(curSql?.[0]?.database ?? '')
  }

  const chatCtx = useChatContext()
  const { outgoingSQLFile, needSetChat2queryCursor } = useHelpCenterContext()

  const sqlConfig = useMemo(() => sqlConfigConverter(dbList), [dbList])

  const [settingVisible, setSettingVisible] = useState(false)
  const [settingChat2data, setSettingChat2data] = useState(false)
  const newFileContent = useNewFileContent()
  const editorOutsideRef = useClickOutside(() => {
    const activeEditor = getEditor(editedSqlFile.id + '')
    if (!activeEditor) return

    hideTooltip(activeEditor.editor)
  })
  const [isAggVisible, setIsAggVisible] = useState(false)
  const editorSettingRef = useRef(editorSetting)

  useEffect(() => {
    editorSettingRef.current = editorSetting

    if (editorSetting.chat2query_init === false) {
      setIsAggVisible(true)
    }
  }, [editorSetting])

  useEffect(() => {
    if (!isRunAll) {
      return
    }

    moveCursor2Tail()
    handleRunSQL(false, false)
    setIsRunAll(false)
  }, [isRunAll])

  useEffect(() => {
    const content = outgoingSQLFile?.content ?? ''
    const activeFileId = editedSqlFile.id
    const activeEditor = getEditor(activeFileId + '')

    if (!activeEditor) return

    if (content !== editedSqlFileRef.current.content) {
      addContent2File(content)
    } else {
      moveCursor2Tail()
    }
  }, [outgoingSQLFile])

  useEffect(() => {
    if (editedSqlFile.id === -1) {
      return
    }

    const editor = getEditor(editedSqlFile.id + '')

    if (!editor) return

    const { state } = editor.editor
    const db = getCurDatabase(state)
    const runDb = getSessionDb(editedSqlFile.id)
    setCurDb(db || runDb)
    setSessionDb(runDb)
  }, [editedSqlFile.id])

  const addContent2File = (content: string) => {
    const activeFileId = editedSqlFile.id
    const activeEditor = getEditor(activeFileId + '')

    if (!activeEditor) return

    const { state } = activeEditor.editor
    const len = content.length
    const file = {
      ...editedSqlFileRef.current,
      content
    }

    setEditedSqlFile(file)
    editedSqlFileRef.current = file

    activeEditor.editor.dispatch({
      changes: {
        from: 0,
        to: state.doc.length,
        insert: content
      },
      selection: { anchor: len, head: len },
      scrollIntoView: true
    })
    activeEditor.editor.focus()
  }

  useEffect(() => {
    if (!needSetChat2queryCursor) {
      return
    }

    moveCursor2Tail()
  }, [needSetChat2queryCursor])

  useEffect(() => {
    window.addEventListener('beforeunload', beforeunloadHandler)

    return () => {
      window.removeEventListener('beforeunload', beforeunloadHandler)
    }
  }, [])

  const beforeunloadHandler = (e: BeforeUnloadEvent) => {
    if (editedSqlFileRef.current.has_modified) {
      saveHandlerBeforeUnload()
      e.preventDefault()
      return 'Content changed'
    }
  }

  const saveHandlerBeforeUnload = async () => {
    setAutoSaveState(AutoSaveStateEnum.Saving)
    addEditorSaveState(editedSqlFileRef.current.id, {
      state: AutoSaveStateEnum.Saving,
      errorMsg: ''
    })

    try {
      const token = auth.getToken()
      const res = await fetch(
        `${dbaasUrl}/api/v1/dataapps/sqleditor/orgs/${orgId}/projects/${projectId}/clusters/${clusterId}/sqlfiles/${editedSqlFileRef.current.id}`,
        {
          method: 'PUT',
          headers: {
            'Content-Type': 'application/json',
            ...(token ? { Authorization: `Bearer ${token}` } : {})
          },
          body: JSON.stringify(editedSqlFileRef.current),
          credentials: 'include',
          keepalive: true
        }
      )

      if (res.status === 200) {
        const json = await res.json()
        if (json.code === 200) {
          editedSqlFileRef.current.has_modified = false
          setEditedSqlFile({
            ...editedSqlFileRef.current,
            has_modified: false
          })

          setTimeout(() => {
            setAutoSaveState(AutoSaveStateEnum.Saved)
            addEditorSaveState(editedSqlFileRef.current.id, {
              state: AutoSaveStateEnum.Saved,
              errorMsg: ''
            })
          }, 500)
        } else {
          throw Error(json.message ?? 'Save failed.')
        }
      } else {
        throw Error('Save failed.')
      }
    } catch (_e) {
      setAutoSaveState(AutoSaveStateEnum.Saved)
      addEditorSaveState(editedSqlFileRef.current.id, {
        state: AutoSaveStateEnum.Saved,
        errorMsg: getErrorMessage(_e)
      })
    }
  }

  const moveCursor2Tail = () => {
    const activeFileId = editedSqlFile.id
    const activeEditor = getEditor(activeFileId + '')

    if (!activeEditor) return

    const { state } = activeEditor.editor
    const len = state.doc.length
    activeEditor.editor.dispatch({
      selection: { anchor: len, head: len },
      scrollIntoView: true
    })
    activeEditor.editor.focus()
  }

  const addNewFile = async () => {
    if (isCreatingFile) {
      return
    }

    eventTracking('SQL Editor Add New SQL Editor File Button Clicked', {
      position: 'Editor'
    })

    setSideMenuActiveIndex(1)

    setIsCreatingFile(true)
    try {
      const fileParams = {
        name: `New query`,
        content: newFileContent
      }

      const createRes = await createSqlEditorFile(orgId, projectId, clusterId, fileParams)
      setIsCreatingFile(false)

      if (createRes.code !== 200) {
        return
      }

      const curFile = {
        id: createRes.data ?? -1,
        ...fileParams
      }

      setEditedSqlFile(curFile)
      editedSqlFileRef.current = curFile

      getSqlFiles(true)
    } catch {
      setIsCreatingFile(false)
    }
  }

  useEffect(() => {
    clearEditorSaveState()
  }, [])

  useEffect(() => {
    if (!editedSqlFilePrev || editedSqlFilePrev.id === -1) {
      return
    }

    if (!editedSqlFilePrev.has_modified) {
      return
    }

    saveFileHandler(editedSqlFilePrev, true)
  }, [editedSqlFile?.id])

  // prepare session in advance before running statement for different sql file
  useEffect(() => {
    if (editedSqlFile?.id) {
      if (!fileSessionMap.get(editedSqlFile.id)) {
        initSessionId(editedSqlFile.id, { orgId, projectId, clusterId })
      }
    }
  }, [editedSqlFile?.id])

  const saveHandler = useRef<() => Promise<void>>()
  saveHandler.current = async () => {
    if (!editedSqlFile || editedSqlFile.id !== editedSqlFileRef.current.id) {
      return
    }

    if (!editedSqlFileRef.current.has_modified) {
      return
    }

    if (editedSqlFileRef.current.id === -1) {
      return
    }

    const content = await saveFileHandler(editedSqlFile)

    const hasChanged = editedSqlFileRef.current.content !== content
    // if changed, the onchange handler will set it
    if (!hasChanged) {
      const file = {
        ...editedSqlFileRef.current,
        has_modified: false
      }

      setEditedSqlFile(file)
      editedSqlFileRef.current = file
    }

    const curFile = sqlFiles.find((f) => f.id === editedSqlFile.id)
    curFile && (curFile.content = content)
  }

  const saveFileHandler = async (sqlFile: SqlFile, needNotify?: boolean) => {
    const { id } = sqlFile
    const editorInst = getEditor(id + '')
    if (!editorInst) {
      return
    }

    if (!needNotify) {
      setAutoSaveState(AutoSaveStateEnum.Saving)
      addEditorSaveState(id, {
        state: AutoSaveStateEnum.Saving,
        errorMsg: ''
      })
    }

    const content = editorInst.editor.state.doc.toString()
    try {
      const res = await updateSqlEditorFile(
        orgId,
        projectId,
        clusterId,
        id,
        {
          ...sqlFile,
          content
        },
        {}
      )

      if (res.code !== 200) {
        setErrorState(id, res.message ?? 'Error')
        needNotify && notifier.error(`${sqlFile.name} saved failed.`)
        return
      }

      setAutoSaveState(AutoSaveStateEnum.Saved)
      addEditorSaveState(id, {
        state: AutoSaveStateEnum.Saved,
        errorMsg: ''
      })

      return content
    } catch (e) {
      const errMsg = getErrorMessage(e)
      const msg =
        errMsg === NetworkErrorMsg
          ? 'Network error: Your changes have saved error, please check your network connection and try again.'
          : errMsg
      setErrorState(id, msg)
      needNotify && notifier.error(`${sqlFile.name} saved failed.`)
    }
  }

  const setErrorState = (id: number, msg?: string) => {
    setAutoSaveState(AutoSaveStateEnum.Failed)
    addEditorSaveState(id, {
      state: AutoSaveStateEnum.Failed,
      errorMsg: msg ?? 'Error'
    })
  }

  const chatEventHandler = useRef<typeof chatCtx.onEvent>()
  chatEventHandler.current = (type: string, view: EditorView, payload?: any) => {
    return chatCtx.onEvent(type, view, { ...payload, file_id: editedSqlFile.id })
  }

  const sessionDBHandler = useRef<() => string>()
  sessionDBHandler.current = () => {
    return getSessionDb(editedSqlFile.id)
  }

  const dbListHandler = useRef<() => string[]>()
  dbListHandler.current = () => {
    return dbList.map((d) => d.name)
  }

  async function handleRunSQL(all: boolean, explain: boolean, beforeRun?: () => void) {
    const activeFileId = editedSqlFile.id
    const activeEditor = getEditor(activeFileId + '')
    if (!activeEditor) return
    let targetStatements: SqlStatement[] = []
    if (all) {
      targetStatements = activeEditor.getAllStatements()
      if (targetStatements.length === 0) return
    } else {
      targetStatements = activeEditor.getCurStatements()
      if (targetStatements[0].content === '') {
        const nearby = activeEditor.getNearbyStatement()
        if (nearby) {
          targetStatements = [nearby]
        } else {
          targetStatements = []
        }
      }
    }

    if (!targetStatements.length) {
      return
    }

    explain ? setIsExplaining(true) : setIsRunning(true)
    props.onRun()
    const aiSql = getAiGeneratedSqlField(activeEditor.editor.state)

    const resultList = targetStatements.reverse().map((s) => {
      let chat2querySid = ''
      const startLine = activeEditor.editor.state.doc.lineAt(s.from).number
      const endLine = activeEditor.editor.state.doc.lineAt(s.to).number
      for (let i = startLine; i <= endLine; i++) {
        if (!isNil(aiSql[i])) {
          chat2querySid = aiSql[i]
        }
      }
      return {
        statement: s,
        status: RequestStatus.Waiting,
        res: {},
        chat2querySid,
        fileId: activeFileId
      }
    })

    setSqlResultList(resultList as any[])

    let hasDDL = false
    const type = explain ? 'explain' : 'execute'
    const len = targetStatements.length
    for (let i = len - 1; i >= 0; i--) {
      const s = targetStatements[i]
      resultList[i].status = RequestStatus.Loading
      setSqlResultList([...resultList] as any[])

      beforeRun?.()

      let resData = await runStatement({ orgId, projectId, clusterId }, activeFileId, type, s)
      if (resData.code === AI_CODE.SESSION_EXPIRED) {
        await initSessionId(activeFileId, { orgId, projectId, clusterId })
        resData = await runStatement({ orgId, projectId, clusterId }, activeFileId, type, s)
      }
      if (s.database) {
        setSessionDb(s.database)
      }

      try {
        const data = resData.data || {}

        aiLog(
          {
            orgId,
            projectId,
            clusterId
          },
          {
            chat2query_sid: resultList[i].chat2querySid,
            event_type: 'exec_sql',
            sql_content: s.content,
            status: resData.code === 200 ? 1 : 2,
            start_time: data.start_time_ms,
            end_time: data.end_time_ms,
            affected_rows: data.row_count,
            error_message: resData.code !== 200 ? resData.message : ''
          },
          activeFileId
        )

        eventTracking(`SQL Editor Executed ${resData.code === 200 ? 'Success' : 'Error'}`)
      } catch (_e) {
        // console.log(e)
      }

      if (resData?.code === 200) {
        resultList[i].status = RequestStatus.Success
        resultList[i].res = resData.data
        setSqlResultList([...resultList] as any[])

        if (type === 'execute' && s.type === 'ddl') {
          hasDDL = true
        }
      } else {
        resultList[i].status = RequestStatus.Error
        resultList[i].res = resData ?? {}
        resultList.splice(0, i)
        setSqlResultList([...resultList] as any[])
        break
      }
    }

    explain ? setIsExplaining(false) : setIsRunning(false)
    setIsDbNeedFresh(hasDDL)

    refreshDbLinter()
  }

  const refreshDbLinter = () => {
    const activeFileId = editedSqlFile.id
    const activeEditor = getEditor(activeFileId + '')
    if (!activeEditor) return

    const len = activeEditor.editor.state.doc.length
    activeEditor.editor.dispatch({
      changes: {
        from: len,
        to: len,
        insert: ' '
      }
    })

    activeEditor.editor.dispatch({
      changes: {
        from: 0,
        to: len + 1,
        insert: activeEditor.editor.state.doc.toString().substring(0, len)
      },
      // keep the cursor position
      selection: {
        anchor: activeEditor.editor.state.selection.main.anchor
      }
    })
  }

  function formatSQL() {
    const activeFileId = editedSqlFile.id
    const activeEditor = getEditor(activeFileId + '')
    if (!activeEditor) return

    const oriDoc = activeEditor.editor.state.doc.toString()
    try {
      const formatted = format(oriDoc, { language: 'mysql' })
      activeEditor.editor.dispatch({
        changes: {
          from: 0,
          to: oriDoc.length,
          insert: formatted
        },
        selection: { anchor: 0, head: 0 }
      })
    } catch (error) {
      console.log(error)
    }
  }

  const dbName = useMemo(() => curDb || sessionDb, [sessionDb, curDb])

  const openEditorSearchPanel = () => {
    const activeFileId = editedSqlFile.id
    const activeEditor = getEditor(activeFileId + '')
    if (!activeEditor) return

    openSearchPanel(activeEditor.editor)
  }

  return (
    <Box
      sx={() => ({
        height: mainPanel.hidden ? '0' : '100%',
        display: 'flex',
        flexDirection: 'column',
        position: 'relative'
      })}
      id="SqlEditor"
    >
      <Flex
        align="center"
        justify="space-between"
        sx={(theme) => ({
          boxShadow: `inset 0 -1px 0 ${theme.colors.carbon[4]}`
        })}
      >
        <Tabs />
        <Flex pr={16} align="center">
          <AutoSaveState />
          <Flex align="center" mr={16} wrap="nowrap">
            {dbName ? <IconDatabase01 /> : <IconDatabaseNone />}
            <Typography
              ml={4}
              variant="body-lg"
              sx={{
                whiteSpace: 'nowrap',
                overflow: 'hidden',
                textOverflow: 'ellipsis',
                maxWidth: '140px'
              }}
            >
              {dbName ? dbName : 'No database used'}
            </Typography>
            {!dbName && (
              <HoverCard shadow="md" width={300}>
                <HoverCard.Target>
                  <Box component={IconInfoCircle} ml={4} sx={{ cursor: 'pointer' }}></Box>
                </HoverCard.Target>
                <HoverCard.Dropdown>
                  <Typography variant="body-lg">To use a particular database:</Typography>
                  <Flex
                    align="center"
                    gap={4}
                    sx={(t) => ({
                      backgroundColor: t.colors.carbon[3],
                      borderRadius: t.defaultRadius,
                      padding: 8,
                      marginTop: 8
                    })}
                  >
                    <Typography variant="action-md" mr={4} fw={400}>
                      USE
                    </Typography>
                    <Typography variant="body-lg">database_name;</Typography>
                  </Flex>
                </HoverCard.Dropdown>
              </HoverCard>
            )}
          </Flex>

          <RunBtn runSqlHandler={handleRunSQL} isRunning={isRunning} isExplaining={isExplaining} />
          {!editorSettingLoading && !editorSetting.is_privacy_allowed && (
            <HoverCard shadow="md" withArrow position="top">
              <HoverCard.Target>
                <ActionIcon
                  variant="default"
                  size="lg"
                  data-mp-event="mp--1691461014086"
                  mr={8}
                  onClick={() => {
                    setIsAggVisible(true)
                  }}
                >
                  <IconAiExplore01 size={16} />
                </ActionIcon>
              </HoverCard.Target>
              <HoverCard.Dropdown>
                <Typography variant="body-sm">Enable AI power for data exploring</Typography>
              </HoverCard.Dropdown>
            </HoverCard>
          )}

          <HotKeys />
          <Menu position="bottom-end">
            <Menu.Target>
              <ActionIcon
                aria-label="Sql Editor Actions"
                variant="default"
                size="lg"
                data-testid="chat2query-control-btn"
                data-mp-event="Sql Editor Ellipse Button Clicked"
              >
                <IconDotsHorizontal size={16} />
              </ActionIcon>
            </Menu.Target>
            <Menu.Dropdown>
              <Menu.Item onClick={formatSQL}>Format SQL</Menu.Item>
              <Menu.Item onClick={openEditorSearchPanel}>Search file</Menu.Item>

              <Menu.Item data-testid="new-sql-file" disabled={isCreatingFile} onClick={addNewFile}>
                New SQL File
              </Menu.Item>

              <Menu.Item onClick={() => setSettingVisible(true)}>Settings</Menu.Item>

              <Menu.Item onClick={() => setSettingChat2data(true)}>Access Chat2Query via API</Menu.Item>
            </Menu.Dropdown>
          </Menu>
        </Flex>
      </Flex>

      <div
        id="EditorContainer"
        ref={editorOutsideRef}
        data-testid="editor-container"
        className={classes.editorContainer}
      >
        <Box className="cm-theme">
          <SQLCodeEditor
            fileId={editedSqlFile.id + ''}
            doc={editedSqlFile.content ?? ''}
            theme={theme}
            sqlConfig={sqlConfig}
            extraExts={[
              sqlAutoCompletion(),
              saveHelper({
                // delay: 3000,
                save: saveHandler.current!
              }),
              onDocChange((_view, _state, sql) => changeHandler.current?.(sql)),
              onSelectionChange((_view, state) => focusChangeHandler.current?.(getCurStatements(state))),
              aiAssistant({
                chat: (req, state) => {
                  if (!editorSettingRef.current.is_privacy_allowed) {
                    return Promise.resolve({
                      status: 'error',
                      message: 'You must agree to the AI Privacy Statement before you can use AI features.'
                    })
                  }

                  return chatCtx.chat({
                    ...req,
                    extra: {
                      ...req.extra,
                      database: getCurDatabase(state)
                    }
                  })
                },
                cancelChat: chatCtx.cancelChat,
                fileId: editedSqlFileRef.current?.id,
                onEvent: chatEventHandler.current!,
                getDbList: dbListHandler.current
              }),
              runHotkeys(
                () =>
                  handleRunSQL(false, false, () => {
                    eventTracking('SQL Editor Executed', {
                      source: 'Hot Key',
                      isRunAll: false
                    })
                  }),
                () =>
                  handleRunSQL(true, false, () => {
                    eventTracking('SQL Editor Executed', {
                      source: 'Hot Key',
                      isRunAll: true
                    })
                  })
              ),
              selectionBgHelper(),
              sqlParser(),
              curSql(),
              curSqlGutter({
                whenHide: (view: EditorView) => isUnifiedMergeViewActive(view.state)
              }),
              useDbLinter({
                whenDisable: () => sessionDBHandler.current?.() !== ''
              }),
              fullWidthCharLinter()
            ]}
          />
        </Box>
      </div>

      {settingVisible && <Settings defaultVisible={true} onClose={() => setSettingVisible(false)} />}

      {settingChat2data && <Chat2DataModal onClose={() => setSettingChat2data(false)} />}

      {isAggVisible && (
        <AIAggModal
          onReset={() => {
            setIsAggVisible(false)
            refetchAiSetting()
          }}
        />
      )}

      <TipsBeforeRouterChange />
    </Box>
  )
}

export default Editor
