import { useLocalStorage } from '@tidbcloud/uikit/hooks'
import React, { useEffect, useRef, useState, Dispatch, SetStateAction, MutableRefObject, useContext } from 'react'
import { useParams } from 'react-router-dom'

import {
  getSqlEditorRowsSetting,
  getSqlEditorFiles,
  createsSqlEditorSession,
  useGetAIUserSetting
} from 'dbaas/services'
import useStores from 'dbaas/stores/useStores'

import { useHelpCenterContext } from '../../HelpCenter/context'
import { getEditor } from '../sql-code-editor/editor-cache'
import { useNewFileContent } from '../SqlFiles/fileContent'
import { SqlRes, SqlFile, UserSetting, OpenedFile, AutoSaveStateEnum } from '../types'
interface LayoutConfig {
  width?: number
  height?: number
  hidden?: boolean
}

export interface SQLEditorLayout {
  leftPanel: LayoutConfig
  mainPanel: LayoutConfig
  bottomPanel: LayoutConfig & {
    leftExpanded?: boolean
  }
}

export const LEFT_PANEL_DEFAULT_WIDTH = 272
export const LEFT_PANEL_MIN_WIDTH = 220
export const BOTTOM_PANEL_DEFAULT_HEIGHT = 200
export const BOTTOM_PANEL_MIN_HEIGHT = 40

const defaultLayout: SQLEditorLayout = {
  leftPanel: {
    width: LEFT_PANEL_DEFAULT_WIDTH
  },
  mainPanel: {},
  bottomPanel: {
    height: BOTTOM_PANEL_DEFAULT_HEIGHT,
    leftExpanded: false
  }
}

export const DEFAULT_QUERY_LOG_LAYOUT = {
  width: 240,
  hidden: false
}

type SetAction<T> = Dispatch<SetStateAction<T>>
interface SQLEditorContext {
  clusterId: string
  orgId: string
  projectId: string
  sqlResultList: SqlRes[] | []
  setSqlResultList: SetAction<SqlRes[] | []>
  dbList: any[]
  setDbList: SetAction<any[]>
  sqlLimit: number
  setSqlLimit: SetAction<number>
  systemShow: boolean
  setSystemShow: SetAction<boolean>
  editorSetting: UserSetting
  setEditorSetting: SetAction<UserSetting>
  editorSettingLoading: boolean
  refetchAiSetting: () => void
  sqlFiles: SqlFile[]
  setSqlFiles: SetAction<SqlFile[]>
  editedSqlFile: SqlFile
  editedSqlFileRef: React.MutableRefObject<SqlFile>
  setEditedSqlFile: SetAction<SqlFile>
  sideMenuActiveIndex: number
  setSideMenuActiveIndex: SetAction<number>
  getSqlFiles: (needMatch?: boolean, selectedFile?: SqlFile) => Promise<void>
  databaseName: string
  setDatabaseName: SetAction<string>
  isCreatingFile: boolean
  setIsCreatingFile: SetAction<boolean>
  editFileQueue: MutableRefObject<SqlFile[]>
  queryLogsH: number
  setQueryLogsH: SetAction<number>
  createSession: (fileId: number, database: string) => Promise<string | undefined>
  isFromImport: boolean
  setIsFromImport: SetAction<boolean>
  newGeneSql: string
  setNewGeneSql: SetAction<string>
  isRunAll: boolean
  setIsRunAll: SetAction<boolean>
  isDbNeedFresh: boolean
  setIsDbNeedFresh: SetAction<boolean>

  layout: SQLEditorLayout
  setLayout: (val: SQLEditorLayout | ((prevState: SQLEditorLayout) => SQLEditorLayout)) => void
  setLeftPanelLayout: (left: SQLEditorLayout['leftPanel']) => void
  resetLeftPanelLayout: () => void
  setMainPanelLayout: (main: SQLEditorLayout['mainPanel']) => void
  setBottomPanelLayout: (bottom: SQLEditorLayout['mainPanel']) => void
  resetBottomPanelLayout: () => void

  queryLogLayout: LayoutConfig
  setQueryLogLayout: (layout: LayoutConfig) => void
  openedFiles: OpenedFile[]
  setOpenedFiles: SetAction<OpenedFile[]>
  scrollEle: SqlFile | null
  setScrollEle: SetAction<SqlFile | null>
  autoSaveErrorMsg: string
  setAutoSaveErrorMsg: SetAction<string>
  autoSaveState: AutoSaveStateEnum | null
  setAutoSaveState: SetAction<AutoSaveStateEnum | null>
}

export const SqlEditorContext = React.createContext<SQLEditorContext | null>(null)

export const SqlEditorProvider: React.FC<React.PropsWithChildren<unknown>> = (props) => {
  const [sqlResultList, setSqlResultList] = useState<SqlRes[] | []>([])
  const [dbList, setDbList] = useState([])
  const [sqlLimit, setSqlLimit] = useState(500)
  const [systemShow, setSystemShow] = useState(false)
  const [editorSetting, setEditorSetting] = useState<UserSetting>({
    chat2query_init: true,
    is_privacy_allowed: false,
    privacy_box_count: 0
  })
  const [editorSettingLoading, setEditorSettingLoading] = useState(true)
  const [sqlFiles, setSqlFiles] = useState<SqlFile[]>([])
  const [editedSqlFile, setEditedSqlFile] = useState<SqlFile>({ id: -1, name: '' })
  const editedSqlFileRef = useRef<SqlFile>({ id: -1, name: '' })
  const [databaseName, setDatabaseName] = useState('')
  const [isCreatingFile, setIsCreatingFile] = useState(false)
  const [sideMenuActiveIndex, setSideMenuActiveIndex] = useState(1)
  const { id: clusterId } = useParams() as { id: string }
  const [queryLogsH, setQueryLogsH] = useState(120)

  const editFileQueue = useRef<SqlFile[]>([])
  const [isFromImport, setIsFromImport] = useState(false)
  const [newGeneSql, setNewGeneSql] = useState('')
  const [isRunAll, setIsRunAll] = useState(false)
  const [isDbNeedFresh, setIsDbNeedFresh] = useState(false)
  const [openedFiles, setOpenedFiles] = useState<OpenedFile[]>([])
  const [scrollEle, setScrollEle] = useState<SqlFile | null>(null)
  const newFileContent = useNewFileContent()
  const [autoSaveErrorMsg, setAutoSaveErrorMsg] = useState('')
  const [autoSaveState, setAutoSaveState] = useState(AutoSaveStateEnum.Saved)

  // for help center
  const {
    setIncomingSQLFile: setCurrentSQLFile,
    dialogOpened: helpCenterDialogOpened,
    setChat2queryReady
  } = useHelpCenterContext()

  const [layout, setLayout] = useLocalStorage<SQLEditorLayout>({
    key: 'sql-editor-layout',
    defaultValue: {
      ...defaultLayout
    },
    deserialize(value) {
      try {
        return JSON.parse(value ?? '')
      } catch (_e) {
        return { ...defaultLayout }
      }
    }
  })

  const [queryLogLayout, setQueryLog] = useLocalStorage<LayoutConfig>({
    key: 'dbaas.querylog-layout',
    defaultValue: {
      ...DEFAULT_QUERY_LOG_LAYOUT
    },
    deserialize(value) {
      try {
        return JSON.parse(value ?? '')
      } catch (_e) {
        return {
          ...DEFAULT_QUERY_LOG_LAYOUT
        }
      }
    }
  })

  const {
    store: { activeProjectId, tenantData, preloadedSQLEditor }
  } = useStores()

  useEffect(() => {
    getSettings()

    localStorage.setItem(
      'chat2queryDefaultData',
      JSON.stringify({
        database: preloadedSQLEditor?.db,
        table: preloadedSQLEditor?.table
      })
    )
  }, [])

  useEffect(() => {
    if (!helpCenterDialogOpened) {
      setCurrentSQLFile(null)
    }

    let oriContent = editedSqlFileRef.current.content ?? ''

    if (editedSqlFileRef.current.content === newFileContent) {
      setCurrentSQLFile({
        ...editedSqlFileRef.current,
        // magic number 4 is the initial sql 'USE ' at the tail of the sql content
        content: oriContent.slice(0, oriContent.length - 4)
      })
      return
    }

    const activeEditor = getEditor(editedSqlFileRef.current.id + '')
    if (!activeEditor) {
      return
    }

    const allSQLs = activeEditor.getAllStatements()

    if (!allSQLs.length) {
      setCurrentSQLFile({ ...editedSqlFileRef.current })
      return
    }

    const lastSqlStatement = allSQLs[allSQLs.length - 1]
    const lastSql = lastSqlStatement.content

    if ((lastSql ?? '').trimEnd().toLowerCase() === 'use') {
      for (let i = lastSqlStatement.from; i < lastSqlStatement.to; i++) {
        oriContent = oriContent.slice(0, lastSqlStatement.from) + oriContent.slice(lastSqlStatement.to + 1)
      }
      setCurrentSQLFile({
        ...editedSqlFileRef.current,
        content: oriContent
      })
    } else {
      setCurrentSQLFile({ ...editedSqlFileRef.current })
    }
  }, [helpCenterDialogOpened, editedSqlFile])

  useEffect(() => {
    return () => {
      setCurrentSQLFile(null)
      setChat2queryReady(false)
    }
  }, [])

  const { refetch: refetchAiSetting } = useGetAIUserSetting(tenantData.id, activeProjectId, {
    query: {
      enabled: !!tenantData.id && !!activeProjectId,
      refetchInterval: false,
      refetchOnWindowFocus: false,
      refetchOnReconnect: false,
      onSuccess: (res) => {
        if (!res || res.code !== 200) {
          return
        }

        const data = res.data || {}

        setEditorSetting(data as UserSetting)
        setEditorSettingLoading(false)
      },
      onSettled: () => {
        setEditorSettingLoading(false)
      }
    }
  })

  const getSettings = async () => {
    const res = await getSqlEditorRowsSetting(tenantData.id, activeProjectId, clusterId)
    if (!res || res.code !== 200) {
      return
    }
    const data = res.data || {}
    setSqlLimit(data.limit || 500)
    setSystemShow(data.show_system_db_schema || false)
  }

  const getSqlFiles = async (needMatch: boolean, selectedFile?: SqlFile) => {
    // @FIXME after code arch change
    setChat2queryReady(false)
    const res = await getSqlEditorFiles(tenantData.id, activeProjectId, clusterId)
    if (res.code !== 200) {
      return
    }

    const list = (res.data || []) as SqlFile[]
    setSqlFiles(list)

    if (selectedFile) {
      setEditedSqlFile(selectedFile)
      editedSqlFileRef.current = selectedFile
      setChat2queryReady(true)
      return
    }

    if (!needMatch) {
      return
    }

    const file = list.find((item) => item.id === editedSqlFileRef.current?.id)
    if (file) {
      const fileContent = {
        sessionId: editedSqlFileRef.current.sessionId,
        ...file
      }
      setEditedSqlFile(fileContent)
      editedSqlFileRef.current = fileContent
    }

    setChat2queryReady(true)
  }

  const createSession = async (fileId: number, database: string) => {
    const res = await createsSqlEditorSession(tenantData.id, activeProjectId, clusterId, {
      database
    })

    const list = sqlFiles.slice()
    const file = list.find((item) => item.id === fileId)
    if (file) {
      file.sessionId = res.data
      setSqlFiles(list)
    }

    const currentId = editedSqlFileRef.current?.id
    if (currentId === fileId || currentId === -1) {
      const data = {
        ...editedSqlFileRef.current,
        sessionId: res.data
      }
      setEditedSqlFile(data)
      editedSqlFileRef.current = data
    }
    return res.data
  }

  const setLeftPanelLayout = (left: SQLEditorLayout['leftPanel']) => {
    setLayout({
      ...layout,
      leftPanel: {
        ...layout.leftPanel,
        ...left
      }
    })
  }

  const resetLeftPanelLayout = () => {
    setLayout({
      ...layout,
      leftPanel: {
        width: LEFT_PANEL_DEFAULT_WIDTH,
        hidden: false
      }
    })
  }

  const setMainPanelLayout = (main: SQLEditorLayout['mainPanel']) => {
    setLayout({
      ...layout,
      mainPanel: {
        ...layout.mainPanel,
        ...main
      }
    })
  }

  const setBottomPanelLayout = (bottom: SQLEditorLayout['bottomPanel']) => {
    setLayout({
      ...layout,
      bottomPanel: {
        ...layout.bottomPanel,
        ...bottom
      }
    })
  }

  const resetBottomPanelLayout = () => {
    setLayout({
      ...layout,
      bottomPanel: {
        height: BOTTOM_PANEL_DEFAULT_HEIGHT,
        hidden: false
      }
    })
  }

  const setQueryLogLayout = (layout: LayoutConfig) => {
    setQueryLog({
      ...queryLogLayout,
      ...layout
    })
  }

  return (
    <SqlEditorContext.Provider
      value={{
        clusterId,
        orgId: tenantData.id,
        projectId: activeProjectId,
        sqlResultList,
        setSqlResultList,
        dbList,
        setDbList,
        sqlLimit,
        setSqlLimit,
        systemShow,
        setSystemShow,
        editorSetting,
        setEditorSetting,
        refetchAiSetting,
        sqlFiles,
        setSqlFiles,
        editedSqlFile,
        editedSqlFileRef,
        setEditedSqlFile,
        getSqlFiles,
        editorSettingLoading,
        databaseName,
        setDatabaseName,
        isCreatingFile,
        setIsCreatingFile,
        sideMenuActiveIndex,
        setSideMenuActiveIndex,
        editFileQueue,
        queryLogsH,
        setQueryLogsH,
        createSession,
        isFromImport,
        setIsFromImport,
        newGeneSql,
        setNewGeneSql,
        setIsRunAll,
        isRunAll,
        isDbNeedFresh,
        setIsDbNeedFresh,
        layout,
        setLayout,
        setLeftPanelLayout,
        resetLeftPanelLayout,
        setMainPanelLayout,
        setBottomPanelLayout,
        resetBottomPanelLayout,
        queryLogLayout,
        setQueryLogLayout,
        openedFiles,
        setOpenedFiles,
        scrollEle,
        setScrollEle,
        autoSaveErrorMsg,
        setAutoSaveErrorMsg,
        autoSaveState,
        setAutoSaveState
      }}
    >
      {props.children}
    </SqlEditorContext.Provider>
  )
}

export const useSQLEditorContext = () => {
  const context = useContext(SqlEditorContext)
  if (!context) {
    throw new Error('SQLContext is empty')
  }
  return context
}
