<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { SelectOption } from 'naive-ui'
import { useI18n } from 'vue-i18n'
import { nanoid } from 'nanoid'
import { cloneDeep } from 'lodash-es'
import type { ValueOf } from 'type-fest'
import PageHeader from './components/page-header.vue'
import ModelDialogueItem from './components/model-dialogue-item.vue'
import FooterOperation from './components/footer-operation.vue'
import { MessageItemInterface, MultiModelDialogueItem, LargeModelItem } from './types'
import {
  fetchGetDebugApplicationInfo,
  fetchGetLargeModelInfo,
  fetchGetLargeModelList,
  fetchSaveAgentApplication,
} from '@/apis/agent-application'
import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import { defaultPersonalAppConfigState } from '@/store/modules/personal-app-config'
import { useThrottleFn } from '@vueuse/core'
import { useUserStore } from '@/store/modules/user'
import { PluginType } from '@/enums/plugin'

const { t } = useI18n()

const router = useRouter()
const currentRoute = useRoute()

const userStore = useUserStore()

const modelDialogueListRef = ref<InstanceType<typeof ModelDialogueItem>[]>([])

const isFullPageLoading = ref(false)
const agentId = ref('')
const agentApplicationConfig = ref<PersonalAppConfigState>(defaultPersonalAppConfigState())
const multiModelDialogueList = ref<MultiModelDialogueItem[]>([])

let modelListOptions = reactive<SelectOption[]>([])

const isAnswerResponseWait = computed(() => {
  return multiModelDialogueList.value.some((modelItem) => modelItem.isAnswerResponseWait)
})

const isHasMessageList = computed(() => {
  return multiModelDialogueList.value.some((modelItem) => modelItem.messageList.size > 0)
})

const isEnableDocumentParse = computed(() => {
  return agentApplicationConfig.value.knowledgeConfig.isDocumentParsing === 'Y'
})

const isEnableUploadImage = computed(() => {
  return agentApplicationConfig.value.unitIds?.includes(PluginType.IMAGE_OCR)
})

onMounted(async () => {
  if (!currentRoute.params.agentId) {
    window.$message.warning(t('multi_model_dialogue_module.not_find_agent'))
    router.replace({ name: 'PersonalSpaceApp' })
    return
  }

  agentId.value = currentRoute.params.agentId as string
  await handleGetApplicationDetail()
  await handleGetLargeModelList()
})

const handleSavePersonalAppConfig = useThrottleFn(
  async (modelDialogueItem: MultiModelDialogueItem) => {
    const { modelNickName, topP, temperature, agentSystem, communicationTurn } = modelDialogueItem

    agentApplicationConfig.value = {
      ...agentApplicationConfig.value,
      baseInfo: { ...agentApplicationConfig.value.baseInfo, agentSystem },
      commModelConfig: {
        ...agentApplicationConfig.value.commModelConfig,
        topP,
        temperature,
        largeModel: modelNickName,
        communicationTurn,
      },
    }

    await handleSaveAgentApplication()
  },
  2000,
  true,
)

function modelDialogueFactory() {
  return {
    id: nanoid(),
    icon: '',
    modelNickName: '',
    topP: 0.1,
    temperature: 1,
    communicationTurn: 5,
    modelPoints: 0,
    agentSystem: '',
    controller: null,
    isAnswerResponseWait: false,
    messageList: new Map<string, MessageItemInterface>(),
  }
}

// 获取应用详情初始化配置
async function handleGetApplicationDetail() {
  isFullPageLoading.value = true

  fetchGetDebugApplicationInfo<PersonalAppConfigState>(agentId.value)
    .then((res) => {
      if (res.code === 0) {
        agentApplicationConfig.value = res.data
        const currentModelConfig = modelDialogueFactory()
        currentModelConfig.topP = agentApplicationConfig.value.commModelConfig.topP
        currentModelConfig.temperature = agentApplicationConfig.value.commModelConfig.temperature
        currentModelConfig.modelNickName = agentApplicationConfig.value.commModelConfig.largeModel
        currentModelConfig.agentSystem = agentApplicationConfig.value.baseInfo.agentSystem
        currentModelConfig.communicationTurn = agentApplicationConfig.value.commModelConfig.communicationTurn

        multiModelDialogueList.value = [
          currentModelConfig,
          cloneDeep({ ...currentModelConfig, id: nanoid(), modelNickName: '' }),
        ]

        handleGetLargeModelInfo()
      }
    })
    .catch(() => {
      router.replace({ name: 'Home' })
    })
}

// 获取大模型列表
async function handleGetLargeModelList() {
  modelListOptions = []
  const res = await fetchGetLargeModelList<LargeModelItem[]>()

  res.data.forEach((item) => {
    modelListOptions.push({
      type: 'group',
      label: item.owner,
      key: item.owner,
      children: item.models.map((model) => ({
        label: model.modelNickName,
        value: model.modelNickName,
        points: model.points || 0,
        style: { fontSize: '12px' },
        icon: item.icon,
      })),
    })
  })
}

// 根据大模型名称获取模型信息
async function handleGetLargeModelInfo() {
  const res = await fetchGetLargeModelInfo<LargeModelItem>(multiModelDialogueList.value[0].modelNickName)

  if (res.code === 0) {
    multiModelDialogueList.value[0].icon = res.data.icon
    multiModelDialogueList.value[0].modelPoints = res.data.models?.[0].points || 0
    isFullPageLoading.value = false
  }
}

// 返回应用设置页
function handleBackAgentSetting() {
  handleBlockMessageResponse()
  router.back()
}

// 添加模型
function handleAddModelDialogue() {
  handleBlockMessageResponse()
  multiModelDialogueList.value.push(cloneDeep({ ...multiModelDialogueList.value[0], id: nanoid(), modelNickName: '' }))
}

// 更新应用配置
async function handleSaveAgentApplication() {
  await fetchSaveAgentApplication<PersonalAppConfigState>(agentApplicationConfig.value)
}

// 移除模型
function handleRemoveModelDialogueItem(modelDialogueItemId: string) {
  multiModelDialogueList.value = multiModelDialogueList.value.filter((item) => item.id !== modelDialogueItemId)
}

// 一键配置
async function handleReplaceAgentConfig(modelDialogueItem: MultiModelDialogueItem) {
  const loadingCtl = window.$message.loading(`${t('common_module.during_operation')}...`)

  const { modelNickName, topP, temperature, agentSystem } = modelDialogueItem

  agentApplicationConfig.value = {
    ...agentApplicationConfig.value,
    baseInfo: { ...agentApplicationConfig.value.baseInfo, agentSystem },
    commModelConfig: {
      ...agentApplicationConfig.value.commModelConfig,
      topP,
      temperature,
      largeModel: modelNickName,
    },
  }

  const res = await fetchSaveAgentApplication(agentApplicationConfig.value).finally(() => {
    loadingCtl.destroy()
  })

  if (res.code === 0) {
    handleBlockMessageResponse()
    router.replace({ name: 'PersonalAppSetting', params: { agentId: agentId.value } })
  }
}

// 开启新会话
function handleResetConversation() {
  if (isHasMessageList.value) {
    window.$message.success(t('multi_model_dialogue_module.open_new_conversation'))
  }

  handleBlockMessageResponse()
}

// 添加提问信息
function handleAddQuestionMessageItem(messageId: string, messageItem: MessageItemInterface) {
  multiModelDialogueList.value.forEach((modelDialogueItem) => {
    const questionMessageItem = {
      ...messageItem,
      nickName: userStore.userInfo.nickName,
      avatar: userStore.userInfo.avatarUrl,
    }

    modelDialogueItem.messageList.set(messageId, questionMessageItem)
  })
}

// 添加回答信息
function handleAddAnswerMessageItem(messageId: string, messageItem: MessageItemInterface, index: number) {
  const questionMessageItem = {
    ...messageItem,
    nickName: multiModelDialogueList.value[index].modelNickName,
    avatar: multiModelDialogueList.value[index].icon,
  }

  multiModelDialogueList.value[index].messageList.set(messageId, questionMessageItem)
}

// 更新对话信息
function handleUpdateSpecifyMessageItem(messageId: string, newObj: Partial<MessageItemInterface>, index: number) {
  const currentMessageItemInfo = multiModelDialogueList.value[index].messageList.get(messageId)

  if (currentMessageItemInfo) {
    Object.entries<ValueOf<typeof newObj>>(newObj).forEach(([key, value]) => {
      if (Object.prototype.hasOwnProperty.call(currentMessageItemInfo, key)) {
        ;(currentMessageItemInfo as any)[key as keyof MessageItemInterface] = value
      }
    })
  }
}

// 删除信息
function handleDeleteMessageItem(messageId: string, index: number) {
  multiModelDialogueList.value[index].messageList.delete(messageId)
}

// 清空历史记录
function handleClearAllMessage() {
  window.$message
    .ctWarning(t('common_module.dialogue_module.clear_message_dialog_content'), t('common_module.tip'))
    .then(() => {
      handleBlockMessageResponse()
      userStore.fetchUpdateEquityInfo()
      window.$message.success(t('common_module.clear_success_message'))
    })
}

// 滚动页面
function handleMessageListScrollToBottom() {
  if (modelDialogueListRef.value) {
    modelDialogueListRef.value.forEach((modelDialogueItemRef, index) => {
      if (!multiModelDialogueList.value?.[index]?.isAnswerResponseWait) {
        return
      }

      modelDialogueItemRef?.scrollToBottom && modelDialogueItemRef.scrollToBottom()
    })
  }
}

// 打断对话
function handleBlockMessageResponse() {
  multiModelDialogueList.value.forEach((modelDialogueItem) => {
    modelDialogueItem.isAnswerResponseWait = false
    modelDialogueItem.controller?.abort()
    modelDialogueItem.messageList.clear()
  })
}
</script>

<template>
  <div class="bg-background-color flex h-full min-h-[720px] w-full min-w-[1280px] flex-col overflow-hidden px-6 py-6">
    <PageHeader
      :agent-title="agentApplicationConfig.baseInfo.agentTitle"
      :agent-desc="agentApplicationConfig.baseInfo.agentDesc"
      :total-num="multiModelDialogueList.length"
      :is-answer-response-wait="isAnswerResponseWait"
      @back="handleBackAgentSetting"
      @add-model-dialogue="handleAddModelDialogue"
    />

    <main
      class="border-inactive-border-color mb-[38px] flex w-full flex-1 flex-col overflow-hidden rounded-[20px] border bg-white pt-6"
    >
      <p class="font-600 text-font-color mb-6 w-full text-center text-base">
        {{ t('common_module.multi_model_debug') }}
      </p>

      <div
        class="grid flex-1 overflow-hidden"
        :style="{ gridTemplateColumns: `repeat(${multiModelDialogueList.length}, minmax(0, 1fr))` }"
      >
        <ModelDialogueItem
          v-for="(modelDialogueItem, index) in multiModelDialogueList"
          :ref="(el) => (modelDialogueListRef[index] = el as InstanceType<typeof ModelDialogueItem>)"
          :key="modelDialogueItem.id"
          :model-dialogue-item="modelDialogueItem"
          :is-current="modelDialogueItem.id === multiModelDialogueList[0].id"
          :model-list-options="modelListOptions"
          :total-num="multiModelDialogueList.length"
          @update-config="handleSavePersonalAppConfig"
          @remove-model-dialogue-item="handleRemoveModelDialogueItem"
          @replace-config="handleReplaceAgentConfig"
          @reset-conversation="handleResetConversation"
        />
      </div>
    </main>

    <FooterOperation
      v-model:multi-model-dialogue-list="multiModelDialogueList"
      :agent-id="agentId"
      :is-answer-response-wait="isAnswerResponseWait"
      :is-enable-document-parse="isEnableDocumentParse"
      :is-enable-upload-image="isEnableUploadImage"
      @add-question-message-item="handleAddQuestionMessageItem"
      @add-answer-message-item="handleAddAnswerMessageItem"
      @update-message-item="handleUpdateSpecifyMessageItem"
      @delete-message-item="handleDeleteMessageItem"
      @clear-all-message="handleClearAllMessage"
      @message-list-scroll-to-bottom="handleMessageListScrollToBottom"
    />

    <Transition name="mask" mode="out-in">
      <div
        v-show="isFullPageLoading"
        class="z-100 absolute inset-0 flex flex-col items-center justify-center bg-[rgba(0,0,0,0.4)]"
      >
        <n-spin :size="38" />

        <div class="text-theme-color mt-[30px] text-[14px]">{{ t('common_module.loading') }}...</div>
      </div>
    </Transition>
  </div>
</template>
