import { loggerService } from '@logger'
import { fetchChatCompletion } from '@renderer/services/ApiService'
import { ConversationService } from '@renderer/services/ConversationService'
import { getAssistantMessage, getUserMessage } from '@renderer/services/MessagesService'
import store from '@renderer/store'
import { updateOneBlock, upsertManyBlocks, upsertOneBlock } from '@renderer/store/messageBlock'
import { newMessagesActions } from '@renderer/store/newMessage'
import { cancelThrottledBlockUpdate, throttledBlockUpdate } from '@renderer/store/thunk/messageThunk'
import type { Assistant, Topic } from '@renderer/types'
import type { Chunk } from '@renderer/types/chunk'
import { ChunkType } from '@renderer/types/chunk'
import { ERROR_I18N_KEY_REQUEST_TIMEOUT, ERROR_I18N_KEY_STREAM_PAUSED } from '@renderer/types/error'
import { AssistantMessageStatus, MessageBlockStatus } from '@renderer/types/newMessage'
import { formatErrorMessage, isAbortError, isTimeoutError } from '@renderer/utils/error'
import { createErrorBlock, createMainTextBlock, createThinkingBlock } from '@renderer/utils/messageUtils/create'
import { cloneDeep } from 'lodash'

const logger = loggerService.withContext('ActionUtils')

export const processMessages = async (
  assistant: Assistant,
  topic: Topic,
  promptContent: string,
  setAskId: (id: string) => void,
  onStream: () => void,
  onFinish: (content: string) => void,
  onError: (error: Error) => void
) => {
  if (!assistant || !topic) return

  try {
    const { message: userMessage, blocks: userBlocks } = getUserMessage({
      assistant,
      topic,
      content: promptContent
    })

    setAskId(userMessage.id)

    store.dispatch(newMessagesActions.addMessage({ topicId: topic.id, message: userMessage }))
    store.dispatch(upsertManyBlocks(userBlocks))

    let textBlockId: string | null = null
    let thinkingBlockId: string | null = null
    let thinkingStartTime: number | null = null
    let textBlockContent: string = ''

    const resolveThinkingDuration = (duration?: number) => {
      if (typeof duration === 'number' && Number.isFinite(duration)) {
        return duration
      }
      if (thinkingStartTime !== null) {
        return Math.max(0, performance.now() - thinkingStartTime)
      }
      return 0
    }

    const assistantMessage = getAssistantMessage({
      assistant,
      topic
    })
    store.dispatch(
      newMessagesActions.addMessage({
        topicId: topic.id,
        message: assistantMessage
      })
    )

    let finished = false

    const newAssistant = cloneDeep(assistant)
    if (!newAssistant.settings) {
      newAssistant.settings = {}
    }
    newAssistant.settings.streamOutput = true
    // 显式关闭这些功能
    newAssistant.webSearchProviderId = undefined
    newAssistant.mcpServers = undefined
    newAssistant.knowledge_bases = undefined
    const { modelMessages, uiMessages } = await ConversationService.prepareMessagesForModel([userMessage], newAssistant)

    await fetchChatCompletion({
      messages: modelMessages,
      assistant: newAssistant,
      requestOptions: {},
      uiMessages: uiMessages,
      onChunkReceived: (chunk: Chunk) => {
        if (finished) {
          return
        }
        switch (chunk.type) {
          case ChunkType.THINKING_START:
            {
              thinkingStartTime = performance.now()
              if (thinkingBlockId) {
                store.dispatch(
                  updateOneBlock({ id: thinkingBlockId, changes: { status: MessageBlockStatus.STREAMING } })
                )
              } else {
                const block = createThinkingBlock(assistantMessage.id, '', {
                  status: MessageBlockStatus.STREAMING
                })
                thinkingBlockId = block.id
                store.dispatch(
                  newMessagesActions.updateMessage({
                    topicId: topic.id,
                    messageId: assistantMessage.id,
                    updates: { blockInstruction: { id: block.id } }
                  })
                )
                store.dispatch(upsertOneBlock(block))
              }
            }
            break
          case ChunkType.THINKING_DELTA:
            {
              if (thinkingBlockId) {
                if (thinkingStartTime === null) {
                  thinkingStartTime = performance.now()
                }
                const thinkingDuration = resolveThinkingDuration(chunk.thinking_millsec)
                throttledBlockUpdate(thinkingBlockId, {
                  content: chunk.text,
                  thinking_millsec: thinkingDuration
                })
              }
              onStream()
            }
            break
          case ChunkType.THINKING_COMPLETE:
            {
              if (thinkingBlockId) {
                const thinkingDuration = resolveThinkingDuration(chunk.thinking_millsec)
                cancelThrottledBlockUpdate(thinkingBlockId)
                store.dispatch(
                  updateOneBlock({
                    id: thinkingBlockId,
                    changes: {
                      content: chunk.text,
                      status: MessageBlockStatus.SUCCESS,
                      thinking_millsec: thinkingDuration
                    }
                  })
                )
                thinkingBlockId = null
              }
              thinkingStartTime = null
            }
            break
          case ChunkType.TEXT_START:
            {
              if (textBlockId) {
                store.dispatch(updateOneBlock({ id: textBlockId, changes: { status: MessageBlockStatus.STREAMING } }))
              } else {
                const block = createMainTextBlock(assistantMessage.id, '', {
                  status: MessageBlockStatus.STREAMING
                })
                textBlockId = block.id
                store.dispatch(
                  newMessagesActions.updateMessage({
                    topicId: topic.id,
                    messageId: assistantMessage.id,
                    updates: { blockInstruction: { id: block.id } }
                  })
                )
                store.dispatch(upsertOneBlock(block))
              }
            }
            break
          case ChunkType.TEXT_DELTA:
            {
              if (textBlockId) {
                throttledBlockUpdate(textBlockId, { content: chunk.text })
              }
              onStream()
              textBlockContent = chunk.text
            }
            break
          case ChunkType.TEXT_COMPLETE:
            {
              if (textBlockId) {
                cancelThrottledBlockUpdate(textBlockId)
                store.dispatch(
                  updateOneBlock({
                    id: textBlockId,
                    changes: { content: chunk.text, status: MessageBlockStatus.SUCCESS }
                  })
                )
                onFinish(chunk.text)
                textBlockContent = chunk.text
                textBlockId = null
              }
            }
            break
          case ChunkType.BLOCK_COMPLETE:
            {
              store.dispatch(
                newMessagesActions.updateMessage({
                  topicId: topic.id,
                  messageId: assistantMessage.id,
                  updates: { status: AssistantMessageStatus.SUCCESS }
                })
              )
            }
            break
          case ChunkType.LLM_RESPONSE_COMPLETE:
            finished = true
            break
          case ChunkType.ERROR:
            {
              const blockId = textBlockId || thinkingBlockId
              thinkingStartTime = null
              if (blockId) {
                store.dispatch(
                  updateOneBlock({
                    id: blockId,
                    changes: {
                      status: isAbortError(chunk.error) ? MessageBlockStatus.PAUSED : MessageBlockStatus.ERROR
                    }
                  })
                )
              }
              const isErrorTypeAbort = isAbortError(chunk.error)
              const isErrorTypeTimeout = isTimeoutError(chunk.error)
              const i18nKey = isErrorTypeAbort
                ? ERROR_I18N_KEY_STREAM_PAUSED
                : isErrorTypeTimeout
                  ? ERROR_I18N_KEY_REQUEST_TIMEOUT
                  : undefined
              const serializableError = {
                name: chunk.error.name,
                message: chunk.error.message || formatErrorMessage(chunk.error),
                originalMessage: chunk.error.message,
                ...(i18nKey && { i18nKey }),
                stack: chunk.error.stack,
                status: chunk.error.status || chunk.error.code,
                requestId: chunk.error.request_id
              }
              const errorBlock = createErrorBlock(assistantMessage.id, serializableError, {
                status: isErrorTypeAbort ? MessageBlockStatus.PAUSED : MessageBlockStatus.ERROR
              })
              store.dispatch(
                newMessagesActions.updateMessage({
                  topicId: topic.id,
                  messageId: assistantMessage.id,
                  updates: { blockInstruction: { id: errorBlock.id } }
                })
              )
              store.dispatch(upsertOneBlock(errorBlock))
              store.dispatch(
                newMessagesActions.updateMessage({
                  topicId: topic.id,
                  messageId: assistantMessage.id,
                  updates: {
                    status: isAbortError(chunk.error) ? AssistantMessageStatus.PAUSED : AssistantMessageStatus.ERROR
                  }
                })
              )
              onFinish(textBlockContent)
            }
            break
        }
      }
    })
  } catch (err) {
    if (isAbortError(err)) return
    onError(err instanceof Error ? err : new Error('An error occurred'))
    logger.error('Error fetching result:', err as Error)
  }
}
