<script setup lang="ts">
import { computed, h, nextTick, ref, shallowRef, useTemplateRef, watch, toValue } from 'vue'
import type { AgentApplicationRecordItem, MessageItemInterface } from '../types'
import { fetchAgentApplicationSelectList, fetchFileUpload } from '@/apis/home-agent'
import { nanoid } from 'nanoid'
import fetchEventStreamSource from '../utils/fetch-event-stream-source'
import { throttle } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useUserStore } from '@/store/modules/user'
import { ChannelType } from '@/enums/channel'
import { UploadStatus } from '@/enums/upload-status'
import { useUploadImage } from '@/composables/useUploadImage'
import { useSystemLanguageStore } from '@/store/modules/system-language'
import { PluginType } from '@/enums/plugin'
import { NAvatar, NEllipsis } from 'naive-ui'
import type { SelectRenderLabel, SelectRenderTag } from 'naive-ui'
import RichTextInputBox from './rich-text-input-box/index.vue'
import type { WorkModeType } from '@/views/home/components/rich-text-input-box/types/index'
import { markdownParser } from '@/components/markdown-render/utils/markdown-parser'

interface Props {
  currentSessionId: string
  messageListLength: number
  isNotShowBackBottomBtn: boolean | undefined
}

const props = defineProps<Props>()
const emit = defineEmits<{
  messageListScrollToBottom: []
  addMessageItem: [messageId: string, messageItem: MessageItemInterface]
  updateSpecifyMessageItem: [messageId: string, newMessageItem: Partial<MessageItemInterface>]
  deleteMessageItem: [messageId: string]
  createNewSession: []
  historyRecordListUpdate: []
}>()

const questionContent = defineModel<string>('questionContent', { required: true })
const currentAgentApplication = defineModel<AgentApplicationRecordItem>('currentAgentApplication', { required: true })
const isAgentResponding = defineModel<boolean>('isAgentResponding', { required: true })
const currentFetchEventSourceController = defineModel<AbortController | null>('currentFetchEventSourceController', {
  required: true,
})
const editorDrawerConfig = defineModel<{ isShow: boolean; content: string }>('editorDrawerConfig', { required: true })

const { t } = useI18n()

const inputFileRef = useTemplateRef<HTMLInputElement | null>('inputFileRef')
const userStore = useUserStore()

const systemLanguageStore = useSystemLanguageStore()

const { uploadImageList, handleLimitUploadImage, handleUploadImage, handleRemoveUploadImage } = useUploadImage()

let fileUploadController = shallowRef<AbortController | null>(null)

const agentApplicationSelectList = ref<AgentApplicationRecordItem[]>([])

const currentLatestMessageItemKeyMap = ref(new Map<'assistant' | 'user', string>())

const currentInputFileInfo = ref({
  url: '',
  fileName: '',
  percentage: 5,
  uploading: false,
})

const currentSceneConfig = ref<{ agentId: string; agentTitle: string; agentDesc: string; agentAvatar: string } | null>(
  null,
)
const workMode = ref<WorkModeType>('ApplicationMode')
const editorModeEnable = ref(false)

const isQuestionSubmitBtnDisabled = computed(() => {
  return (
    questionContent.value.trim().length === 0 ||
    isAgentResponding.value ||
    currentInputFileInfo.value.uploading ||
    uploadImageList.value.some((imageItem) => imageItem.status !== UploadStatus.FINISHED)
  )
})

const isEnglishLanguage = computed(() => systemLanguageStore.currentLanguageInfo.key === 'en')

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

;(function () {
  getAgentApplicationSelectList()
})()

const messageListScrollToBottomThrottle = throttle(
  () => {
    emit('messageListScrollToBottom')
  },
  100,
  { leading: true, trailing: true },
)

watch(
  () => uploadImageList.value.length,
  () => {
    nextTick(() => {
      emit('messageListScrollToBottom')
    })
  },
)

function getAgentApplicationSelectList() {
  fetchAgentApplicationSelectList<AgentApplicationRecordItem[]>().then((res) => {
    if (res.data && Array.isArray(res.data)) {
      agentApplicationSelectList.value = res.data.map((item) => ({ ...item, class: 'application-menu-item' }))
    }

    currentAgentApplication.value = res.data[0]
  })
}

function handleCreateNewSession() {
  if (isAgentResponding.value) {
    window.$message.ctWarning(t('home_module.interrupt_dialogue_prompt')).then(() => {
      currentFetchEventSourceController.value && currentFetchEventSourceController.value.abort()

      isAgentResponding.value = false

      emit('createNewSession')
      handleFileUploadCancel()

      /* 延时，避免页面刷新后，历史记录列表未刷新 */
      setTimeout(() => {
        emit('historyRecordListUpdate')
      }, 300)
    })
  } else {
    if (props.messageListLength === 0) {
      window.$message.warning(t('home_module.currently_in_the_latest_session'))

      return
    }

    emit('createNewSession')
    handleFileUploadCancel()
  }
}

function questionSubmit(question: string) {
  const latestUserMessageKey = nanoid()
  const latestAssistantMessageKey = nanoid()

  currentLatestMessageItemKeyMap.value.set('user', latestUserMessageKey)
  currentLatestMessageItemKeyMap.value.set('assistant', latestAssistantMessageKey)

  emit('addMessageItem', latestUserMessageKey, {
    role: 'user',
    agentId: '',
    content: question.trim(),
    timestamp: Date.now(),
    isAnswerLoading: false,
    avatar: '',
    name: '',
    imageUrl: uploadImageList.value?.[0]?.url || '',
    reasoningContent: '',
  })

  const agentAvatar = currentAgentApplication.value.agentAvatar
  const agentName = currentAgentApplication.value.agentTitle

  nextTick(() => {
    emit('addMessageItem', latestAssistantMessageKey, {
      role: 'assistant',
      agentId: '',
      content: '',
      timestamp: Date.now(),
      isAnswerLoading: true,
      avatar: agentAvatar,
      name: agentName,
      pluginName: '',
      reasoningContent: '',
    })

    setTimeout(() => {
      emit('messageListScrollToBottom')
    }, 200)
  })

  isAgentResponding.value = true

  // let isFirstClip = true
  let messageContent = ''
  let reasoningContent = ''

  const agentId =
    workMode.value === 'ApplicationMode' ? currentAgentApplication.value.agentId : currentSceneConfig.value?.agentId

  currentFetchEventSourceController.value = fetchEventStreamSource(
    '/agentApplicationRest/callAgentApplication.json',
    {
      dialogsId: props.currentSessionId, //会话ID
      agentId: agentId, //应用ID
      input: question.trim(), //提问文本
      fileUrls: currentInputFileInfo.value.url ? [currentInputFileInfo.value.url] : [],
      channel: ChannelType.index,
      imageUrl: uploadImageList.value?.[0]?.url || '', // 图片链接
    },
    {
      onResponse: (data) => {
        // 推理内容
        if (data.reasoningContent) {
          reasoningContent += data.reasoningContent

          emit('updateSpecifyMessageItem', currentLatestMessageItemKeyMap.value.get('assistant')!, {
            reasoningContent: reasoningContent,
          })

          props.isNotShowBackBottomBtn && messageListScrollToBottomThrottle()
        }

        // 回复消息
        if (data.message) {
          messageContent += data.message

          emit('updateSpecifyMessageItem', currentLatestMessageItemKeyMap.value.get('assistant')!, {
            content: messageContent,
          })

          props.isNotShowBackBottomBtn && messageListScrollToBottomThrottle()
        }

        // 插件
        if (data.function && data.function.name) {
          emit('updateSpecifyMessageItem', currentLatestMessageItemKeyMap.value.get('assistant')!, {
            pluginName: data.function.name,
          })

          props.isNotShowBackBottomBtn && messageListScrollToBottomThrottle()
        }
      },
      onend: () => {
        setTimeout(async () => {
          emit('updateSpecifyMessageItem', currentLatestMessageItemKeyMap.value.get('assistant')!, {
            isAnswerLoading: false,
          })

          props.isNotShowBackBottomBtn && messageListScrollToBottomThrottle()

          if (editorModeEnable.value) {
            editorDrawerConfig.value = {
              isShow: true,
              content: await markdownParser(messageContent),
            }
          }
        }, 100)
      },
      onclose: () => {
        isAgentResponding.value = false

        nextTick(() => {
          emit('historyRecordListUpdate')

          userStore.fetchUpdateEquityInfo()
        })
      },
      onerror: (err) => {
        err.message && window.$message.error(err.message)

        emit('deleteMessageItem', currentLatestMessageItemKeyMap.value.get('assistant')!)
      },
    },
  )

  questionContent.value = ''
  uploadImageList.value = []
}

// function handleQuestionSubmitEnter(event: KeyboardEvent) {
//   if (event.key === 'Enter' && !event.shiftKey) {
//     event.preventDefault()

//     if (!isQuestionSubmitBtnDisabled.value) {
//       questionSubmit()
//     }
//   }
// }

function handleFileUploadPopup() {
  inputFileRef.value && inputFileRef.value.click()
}

function handleFileUpload(e: Event) {
  const target = e.target as HTMLInputElement
  const file = target.files && target.files[0]

  if (file) {
    if (!['.pdf', '.txt', '.md', '.doc', '.docx'].some((fileType) => file.name.endsWith(fileType))) {
      window.$message.warning(t('personal_settings_module.the_file_type_cannot_be_uploaded'))

      return
    }

    if (file.size === 0) {
      window.$message.warning(t('equity_module.file_empty_tip'))
      return
    } else if (file.size > 1024 * 1024 * 10) {
      window.$message.warning(t('common_module.file_size_limit', { size: '10MB' }))
      return
    }

    currentInputFileInfo.value = {
      fileName: file.name,
      url: URL.createObjectURL(file),
      percentage: 5,
      uploading: true,
    }

    fileUploadController.value && fileUploadController.value.abort()

    fileUploadController.value = new AbortController()

    const formData = new FormData()
    formData.append('file', file)

    fetchFileUpload<string>(formData, {
      onUploadProgress: (progressEvent) => {
        const percentage = Number.parseInt((progressEvent?.progress || 0).toFixed(2)) * 100

        currentInputFileInfo.value.percentage = percentage < 5 ? 5 : percentage

        if (progressEvent?.progress === 1) {
          currentInputFileInfo.value.uploading = false
        }
      },
      signal: fileUploadController.value.signal,
    }).then((res) => {
      currentInputFileInfo.value.url = res.data
    })
  }
}

function handleFileUploadCancel() {
  currentInputFileInfo.value = {
    url: '',
    fileName: '',
    percentage: 0,
    uploading: false,
  }

  inputFileRef.value && (inputFileRef.value.value = '')

  if (fileUploadController.value) {
    fileUploadController.value.abort()
    fileUploadController.value = null
  }
}

function handleFileUploadReplace() {
  handleFileUploadPopup()
}

function handleSelectImage(cb: () => void) {
  if (uploadImageList.value.length > 0) {
    window.$message.ctWarning('', t('common_module.dialogue_module.overwrite_image_tip')).then(() => {
      cb()
    })
    return
  }

  cb()
}

const selectMenuRenderLabel: SelectRenderLabel = (option) => {
  return h(
    'div',
    {
      style: {
        display: 'flex',
        alignItems: 'center',
        width: '100%',
      },
    },
    [
      h(NAvatar, {
        src: option.agentAvatar as string,
        size: 22,
        color: '#fff',
        'object-fit': 'contain',
        style: {
          marginRight: '6px',
          flexShrink: 0,
        },
      }),
      h(NEllipsis, { style: { width: '100%', marginRight: '6px' } }, { default: () => option.agentTitle }),
      h(
        'span',
        { style: { justifySelf: 'end', fontSize: '12px', color: '#0B7DFF', textAlign: 'end', marginLeft: 'auto' } },
        [`${option.points as string}${t('equity_module.points2')}/${t('common_module.time')}`],
      ),
    ],
  )
}

const selectMenuRenderTag: SelectRenderTag = ({ option }) => {
  return h(
    'div',
    {
      style: {
        display: 'flex',
        alignItems: 'center',
      },
    },
    [
      h(NAvatar, {
        src: option.agentAvatar as string,
        size: 22,
        color: '#fff',
        'object-fit': 'contain',
        style: {
          marginRight: '6px',
          flexShrink: 0,
        },
      }),
      h(NEllipsis, { style: { width: '100%' } }, { default: () => option.agentTitle }),
    ],
  )
}

function onAgentApplicationInfoUpdate(_key: string, option: AgentApplicationRecordItem) {
  currentAgentApplication.value = toValue(option)

  if (currentInputFileInfo.value.url) {
    currentInputFileInfo.value = {
      url: '',
      fileName: '',
      percentage: 5,
      uploading: false,
    }
  }

  if (uploadImageList.value.length > 0) {
    uploadImageList.value = []
  }

  if (questionContent.value) {
    questionContent.value = ''
  }
}

function onWorkModeChange(mode: WorkModeType) {
  workMode.value = mode
}

defineExpose({
  clearSessionReferenceFile: handleFileUploadCancel,
})
</script>

<template>
  <div class="mx-auto mt-auto w-[750px] pr-[10px]">
    <div class="mb-[10px] flex select-none justify-between">
      <div>
        <Transition mode="out-in">
          <div v-if="workMode === 'ApplicationMode'" class="w-[190px]">
            <n-select
              :value="currentAgentApplication.agentId"
              class="application-menu"
              :options="agentApplicationSelectList"
              :render-label="selectMenuRenderLabel"
              :render-tag="selectMenuRenderTag"
              label-field="agentTitle"
              value-field="agentId"
              placement="top-start"
              :consistent-menu-width="false"
              :menu-props="{ style: { maxWidth: '220px' }, class: 'application-menu-popup' }"
              :on-update:value="onAgentApplicationInfoUpdate"
            />
          </div>

          <div
            v-else-if="workMode === 'SceneMode'"
            class="flex h-[34px] w-[190px] items-center rounded-[10px] border border-[#9ea3ff] px-[12px]"
          >
            <!-- <i class="iconfont icon-xiezuo mr-[6px] text-[18px]" :class="{ [`${currentSceneConfig?.icon}`]: true }"></i> -->
            <NAvatar
              :src="currentSceneConfig?.agentAvatar"
              :size="22"
              object-fit="contain"
              class="mr-[6px] shrink-0"
            ></NAvatar>

            <span class="flex-1 overflow-hidden text-[14px]">
              <NEllipsis>{{ currentSceneConfig?.agentTitle }}</NEllipsis>
            </span>

            <i
              class="iconfont icon-close hover:text-theme-color ml-[4px] cursor-pointer text-[14px] transition"
              @click="onWorkModeChange('ApplicationMode')"
            ></i>
          </div>
        </Transition>
      </div>

      <div class="flex items-center">
        <Transition mode="out-in">
          <div v-if="!currentInputFileInfo.fileName">
            <n-popover trigger="hover">
              <template #trigger>
                <n-button
                  v-show="currentAgentApplication.isDocumentParsing === 'Y'"
                  class="application-select-btn !mr-[8px] !h-[34px] !rounded-[10px] !p-0"
                  @click="handleFileUploadPopup"
                >
                  <div class="box-border flex w-full items-center justify-between px-[12px]">
                    <i class="iconfont icon-upload mr-[5px] text-[14px]"></i>
                    <span class="text-[14px]">{{ t('personal_settings_module.upload_file') }}</span>
                  </div>
                </n-button>
              </template>
              <span>{{ t('personal_settings_module.file_type_restriction_doc') }}</span>
            </n-popover>
          </div>

          <div v-else class="relative !mr-[14px] flex !h-[34px] items-center rounded-[10px] bg-[#ECEFFF] px-[14px]">
            <div
              class="mr-[5px] h-[18px] w-[18px] bg-[url('https://gsst-poe-sit.gz.bcebos.com/icon/doc.svg')] bg-contain bg-no-repeat"
            ></div>

            <div
              class="text-[14px]"
              :class="{
                'w-[90px]': isEnglishLanguage && isEnableUploadImage,
                'w-[190px]': !isEnglishLanguage && isEnableUploadImage,
                'w-[240px]': !isEnableUploadImage,
              }"
            >
              <NEllipsis :tooltip="{ width: 400 }">
                {{ currentInputFileInfo.fileName }}
              </NEllipsis>
            </div>

            <div class="ml-[10px]">
              <i
                class="iconfont icon-huanyihuan hover:text-theme-color mr-[10px] cursor-pointer text-[14px] transition"
                @click="handleFileUploadReplace"
              ></i>
              <i
                class="iconfont icon-close hover:text-theme-color cursor-pointer text-[14px] transition"
                @click="handleFileUploadCancel"
              ></i>
            </div>

            <div v-show="currentInputFileInfo.uploading" class="absolute bottom-[1px] left-[40px] w-[86%]">
              <n-progress
                type="line"
                :percentage="currentInputFileInfo.percentage"
                :show-indicator="false"
                processing
                :fill-border-radius="0"
                :border-radius="0"
                :height="3"
                rail-color="#DCDCDC"
              />
            </div>
          </div>
        </Transition>

        <n-upload
          :show-file-list="false"
          accept="image/png, image/jpeg, image/jpg, image/webp"
          abstract
          @before-upload="handleLimitUploadImage"
          @change="handleUploadImage"
        >
          <n-upload-trigger #="{ handleClick }" abstract>
            <n-popover style="width: 210px" trigger="hover">
              <template #trigger>
                <n-button
                  v-show="isEnableUploadImage"
                  class="upload-image-btn !mr-[8px] !h-[34px] !rounded-[10px] !p-0"
                  @click="handleSelectImage(handleClick)"
                >
                  <div class="box-border flex w-full items-center justify-between px-[12px]">
                    <i class="iconfont icon-upload-image mr-[5px] text-[14px]"></i>
                    <span class="text-[14px]">{{ t('common_module.upload_image') }}</span>
                  </div>
                </n-button>
              </template>

              <span>{{ t('common_module.dialogue_module.upload_image_limit') }}</span>
            </n-popover>
          </n-upload-trigger>
        </n-upload>

        <n-button class="application-select-btn !h-[34px] !rounded-[10px] !p-0" @click="handleCreateNewSession">
          <div class="box-border flex w-full items-center justify-between px-[12px]">
            <i class="iconfont icon-session mr-[5px] text-[14px]"></i>
            <span class="text-[14px]">{{ t('home_module.starting_a_new_session') }}</span>
          </div>
        </n-button>

        <input
          ref="inputFileRef"
          type="file"
          class="hidden"
          accept=".pdf,.txt,.md,.doc,.docx"
          @change="handleFileUpload"
        />
      </div>
    </div>

    <div class="relative">
      <RichTextInputBox
        v-model:question-content="questionContent"
        v-model:current-scene-config="currentSceneConfig"
        v-model:editor-mode-enable="editorModeEnable"
        :is-question-submit-btn-disabled="isQuestionSubmitBtnDisabled"
        :work-mode="workMode"
        :upload-image-list="uploadImageList"
        @submit-question="questionSubmit"
        @work-mode-switch="onWorkModeChange"
        @remove-upload-image="handleRemoveUploadImage"
      />

      <!-- <n-input
        v-model:value.trim="questionContent"
        class="content-input"
        type="textarea"
        :autosize="{ minRows: isHasUploadImage ? 2 : 4, maxRows: isHasUploadImage ? 2 : 4 }"
        :class="isHasUploadImage ? 'carry-image' : ''"
        :placeholder="t('home_module.please_enter_a_question')"
        @keydown="handleQuestionSubmitEnter"
      >
        <template #suffix>
          <div :class="isHasUploadImage ? 'absolute bottom-[9px] right-[11px]' : 'flex h-full items-end pb-[10px]'">
            <n-button type="primary" :disabled="isQuestionSubmitBtnDisabled" @click="() => questionSubmit()">
              <i class="iconfont icon-send-icon"></i>
            </n-button>
          </div>
        </template>
      </n-input> -->

      <!-- <div v-show="isHasUploadImage" class="absolute bottom-[9px] left-[11px] flex gap-[11px]">
        <div
          v-for="uploadImageItem in uploadImageList"
          :key="uploadImageItem.id"
          class="border-inactive-border-color relative h-[66px] w-[66px] rounded-[10px] border bg-white"
          :class="{ 'border-[#F25744]!': uploadImageItem.status === UploadStatus.ERROR }"
        >
          <div
            class="absolute right-[-4px] top-[-4px] flex h-4 w-4 cursor-pointer items-center justify-center rounded-full bg-[rgba(0,0,0,0.55)] hover:opacity-80"
            @click="handleRemoveUploadImage(uploadImageItem.id)"
          >
            <CloseSmall theme="outline" size="16" fill="#fff" />
          </div>

          <div class="flex h-full w-full items-center justify-center overflow-hidden rounded-[10px]">
            <n-spin v-show="uploadImageItem.status === UploadStatus.UPLOADING" :size="20" />

            <n-image
              v-show="uploadImageItem.status === UploadStatus.FINISHED"
              width="100"
              object-fit="contain"
              :src="uploadImageItem.url"
              preview-disabled
              class="h-full w-full flex-shrink-0"
            />
          </div>
        </div>
      </div> -->
    </div>

    <div class="mt-[14px] text-center text-[13px] text-[#999]">
      {{ t('home_module.all_of_the_above_content_is_generated_by_ai_and_is_for_reference_only') }}
    </div>
  </div>
</template>

<style lang="scss" scoped>
.content-input {
  :deep(.n-input__border) {
    border-color: #9ea3ff;
  }
}

:deep(.carry-image .n-input-wrapper) {
  padding-bottom: 84px;
}

:deep(.upload-image-btn),
:deep(.application-select-btn) {
  .n-button__border {
    border-color: #9ea3ff;
  }
}

.v-enter-active,
.v-leave-active {
  transition-timing-function: ease-in-out;
  transition-duration: 0.2s;
  transition-property: opacity;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
}

:deep(.application-menu) {
  .n-base-selection {
    border-radius: 10px;
  }

  .n-base-selection__border {
    border-color: #9ea3ff;
  }

  .n-base-selection-label {
    height: 34px;
  }

  .application-menu-inner .select-item-image {
    width: 16px;
    height: 16px;
  }
}

:global(.application-menu-item .n-base-select-option__content) {
  width: 100%;
}
</style>
