import { EditorView } from 'codemirror'
import { capitalize } from 'lodash-es'
import { useCallback, useMemo } from 'react'
import { format } from 'sql-formatter'

import { eventTracking } from 'dbaas/utils/tracking'

import { ClusterInfo, chat2sql, sqlAutoFix, sqlRefine, cancelChat2sql, aiLog } from '../api/dbaas'
import { ChatReq, ChatRes } from '../sql-code-editor/extensions/ai-assistant'
import { getAiGeneratedSqlField } from '../sql-code-editor/extensions/ai-generated-sql'

import { ChatContext } from './chat-context'
import { useSQLEditorContext } from './editor-context'

async function chatSql(clusterInfo: ClusterInfo, req: ChatReq & { file_id: number }): Promise<ChatRes> {
  if (req.refContent) {
    if (req.prompt === 'Fix code error') {
      return sqlAutoFix(clusterInfo, {
        database: req.extra.database,
        sql: req.refContent,
        raw_question: '',
        file_id: req.file_id,
        sql_error: req.extra.sql_error
      })
    }
    return sqlRefine(clusterInfo, {
      database: req.extra.database,
      sql: req.refContent,
      raw_question: '',
      feedback: req.prompt,
      file_id: req.file_id
    })
  }

  return chat2sql(clusterInfo, {
    database: req.extra.database,
    raw_question: req.prompt,
    file_id: req.file_id
  })
}

enum NO_DB_EVENT {
  ADD_USE_DB = 'add.use_db',
  USE_DB_SEND = 'use_db.send',
  USE_DB_OPEN = 'use_db.open'
}

const NoDbEventMsgMap = {
  'add.use_db': 'SQL Editor Add Use Database Button Clicked',
  'use_db.send': 'SQL Editor Generate Button Clicked When No Database',
  'use_db.open': 'SQL Editor Ai Prompt Opened When No Database'
}

const onEvent = (type: string, view: EditorView, payload: any) => {
  if (type) {
    let eventMsg = `SQL Editor ${capitalize(type)} AI`
    let isAccept = -1
    switch (type) {
      case 'accept':
        const { newFromLine, newToLine, preFromLine, preToLine, session_id } = payload
        const fillData = new Array(newToLine - newFromLine + 1).fill(session_id)
        const aiSqlData = getAiGeneratedSqlField(view.state)
        aiSqlData.splice(newFromLine, preToLine - preFromLine + 1, ...fillData)

        isAccept = 1
        break
      case 'reject':
        isAccept = 0
        break
      case 'regenerate':
        eventMsg += 'Result'
        isAccept = 0
        break
      case 'cancel':
        payload.prompt && (isAccept = 0)
        break
      case 'fix':
        eventMsg = 'SQL Editor Fix By Chat2Query'
        break
      default:
        if (NoDbEventMsgMap[type as NO_DB_EVENT]) {
          eventMsg = NoDbEventMsgMap[type as NO_DB_EVENT]
        }
        break
    }

    if (['generate', 'regenerate', 'fix'].includes(type)) {
      eventTracking(eventMsg, { status: payload.AiResultStatus, duration: payload.duration })
    } else {
      eventTracking(eventMsg)
    }

    const { orgId, projectId, clusterId, session_id, content, prompt } = payload
    if (isAccept !== -1 && orgId) {
      const log = {
        session_id: '',
        chat2query_sid: session_id,
        event_type: 'sql_accept',
        is_accept: !!isAccept,
        sql_content: content,
        user_question: prompt,
        prompt_version: ''
      }
      aiLog({ orgId, projectId, clusterId }, log, payload.file_id)
    }
  }
}

export function ChatProvider(props: { children: React.ReactNode }) {
  const { orgId, projectId, clusterId, editedSqlFileRef } = useSQLEditorContext()

  const chat = useCallback(
    (req: ChatReq) => {
      return chatSql(
        { orgId, projectId, clusterId },
        {
          file_id: editedSqlFileRef.current.id,
          ...req
        }
      ).then((data) => {
        if (data.status === 'success') {
          return {
            ...data,
            message: format(data.message, { language: 'mysql' })
          }
        } else {
          return data
        }
      })
    },
    [orgId, projectId, clusterId]
  )
  const ctxValue = useMemo(
    () => ({
      chat,
      cancelChat: cancelChat2sql,
      fileId: editedSqlFileRef.current.id,
      onEvent
    }),
    []
  )

  return <ChatContext.Provider value={ctxValue}>{props.children}</ChatContext.Provider>
}
