<script setup lang="ts">
import { computed, nextTick, ref, shallowRef, toValue, useTemplateRef } 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'

interface Props {
  currentSessionId: string
  messageListLength: number
}

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 { t } = useI18n()

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

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

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

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

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

const isQuestionSubmitBtnDisabled = computed(() => {
  return questionContent.value.trim().length === 0 || isAgentResponding.value || currentInputFileInfo.value.uploading
})

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

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

function getAgentApplicationSelectList() {
  fetchAgentApplicationSelectList<AgentApplicationRecordItem[]>().then((res) => {
    agentApplicationSelectList.value = res.data

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

function handleApplicationSelectMenuSwitchShow() {
  isShowApplicationSelectMenu.value = !isShowApplicationSelectMenu.value
}

function handleApplicationSelectBtnBlur() {
  setTimeout(() => {
    isShowApplicationSelectMenu.value = false
  }, 100)
}

function handleApplicationChange(agentApplicationItem: AgentApplicationRecordItem) {
  if (currentAgentApplication.value.agentId === agentApplicationItem.agentId) {
    return
  }

  currentAgentApplication.value = toValue(agentApplicationItem)
}

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() {
  const latestUserMessageKey = nanoid()
  const latestAssistantMessageKey = nanoid()

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

  emit('addMessageItem', latestUserMessageKey, {
    role: 'user',
    agentId: '',
    content: questionContent.value.trim(),
    timestamp: Date.now(),
    isAnswerLoading: false,
    avatar: '',
    name: '',
  })

  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,
    })

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

  isAgentResponding.value = true

  // let isFirstClip = true
  let messageContent = ''

  currentFetchEventSourceController.value = fetchEventStreamSource(
    '/agentApplicationRest/callAgentApplication.json',
    {
      dialogsId: props.currentSessionId, //会话ID
      agentId: currentAgentApplication.value.agentId, //应用ID
      input: questionContent.value.trim(), //提问文本
      fileUrls: currentInputFileInfo.value.url ? [currentInputFileInfo.value.url] : [],
    },
    {
      onmessage: (message) => {
        messageContent += message

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

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

          messageListScrollToBottomThrottle()
        }, 500)
      },
      onclose: () => {
        isAgentResponding.value = false

        nextTick(() => {
          emit('historyRecordListUpdate')
        })
      },
      onerror: (err) => {
        err.message && window.$message.error(err.message)

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

  questionContent.value = ''
}

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

    if (questionContent.value.trim().length > 0 && !isAgentResponding.value && !currentInputFileInfo.value.uploading) {
      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()
}

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

<template>
  <div class="mt-auto pr-[10px]">
    <div class="mb-[10px] flex select-none justify-between">
      <!-- <div
        class="relative box-border flex h-[34px] w-[145px] cursor-pointer items-center rounded-[10px] border border-[#9EA3FF] px-[12px]"
      >
        <div class="flex w-full items-center justify-between">
          <div class="flex items-center">
            <div class="bg-px-home-cuixiang_icon-png mr-[5px] h-[16px] w-[16px]"></div>
            <div class="text-[14px]">萃想AI</div>
          </div>

          <Up theme="outline" size="18" fill="#333" :stroke-width="3" />
        </div>

        <div
          class="absolute -top-[10px] left-1/2 w-[200px] -translate-x-1/2 -translate-y-full rounded-[10px] bg-[#fff] p-[6px] shadow-md"
        >
          hello
        </div>
      </div> -->

      <div class="relative">
        <n-button
          class="application-select-btn !h-[34px] !rounded-[10px] !p-0"
          @click="handleApplicationSelectMenuSwitchShow"
          @blur="handleApplicationSelectBtnBlur"
        >
          <div class="box-border flex !w-[160px] w-full items-center justify-between px-[12px]">
            <div class="mr-[5px] flex flex-1 items-center overflow-hidden">
              <div
                class="mr-[10px] h-[16px] w-[16px] rounded-[6px] bg-cover bg-no-repeat"
                :class="{ 'bg-px-home-cuixiang_icon-png': !currentAgentApplication.agentAvatar }"
                :style="{ backgroundImage: `url(${currentAgentApplication.agentAvatar})` }"
              ></div>
              <div class="flex-1 truncate text-start text-[14px]">{{ currentAgentApplication.agentTitle || '-' }}</div>
            </div>

            <i
              class="iconfont icon-left rotate-90 text-[12px] transition-[rotate] duration-300 ease-in-out"
              :class="{ '!rotate-270': isShowApplicationSelectMenu }"
            ></i>
          </div>
        </n-button>

        <Transition name="application-select-menu">
          <ul
            v-show="isShowApplicationSelectMenu"
            class="absolute -top-[10px] left-1/2 w-[200px] -translate-x-1/2 -translate-y-full rounded-[6px] bg-[#fff] p-[10px] pb-[4px] pr-0 shadow-md"
          >
            <n-virtual-list
              style="max-height: 200px"
              :item-size="35"
              :items="agentApplicationSelectList"
              key-field="agentId"
              items-style="padding-right: 10px;"
            >
              <template #default="{ item }">
                <li
                  class="relative mb-[6px] flex cursor-pointer items-center overflow-hidden rounded-[4px] bg-[#f3f3f5] px-[10px] py-[4px] transition hover:bg-[#E7E7E7]"
                  @click="handleApplicationChange(item)"
                >
                  <div
                    class="mr-[10px] h-[16px] w-[16px] rounded-[6px] bg-cover bg-no-repeat"
                    :class="{ 'bg-px-home-cuixiang_icon-png': !item.agentAvatar }"
                    :style="{ backgroundImage: `url(${item.agentAvatar})` }"
                  ></div>
                  <div class="flex-1 overflow-hidden">
                    <n-ellipsis :tooltip="{ placement: 'right' }">
                      {{ item.agentTitle }}
                    </n-ellipsis>
                  </div>

                  <i
                    v-if="item.agentId === currentAgentApplication.agentId"
                    class="iconfont icon-xuanze absolute bottom-0 right-0 text-[14px] text-[#777ef9]"
                  ></i>
                </li>
              </template>
            </n-virtual-list>
          </ul>
        </Transition>
      </div>

      <div class="flex items-center">
        <Transition name="file-upload" mode="out-in">
          <div v-if="!currentInputFileInfo.fileName">
            <n-popover trigger="hover">
              <template #trigger>
                <n-button
                  class="application-select-btn !mr-[14px] !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="w-[260px] text-[14px]">
              <n-ellipsis :tooltip="{ width: 400 }">
                {{ currentInputFileInfo.fileName }}
              </n-ellipsis>
            </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-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>
      <n-input
        v-model:value.trim="questionContent"
        class="content-input"
        type="textarea"
        :autosize="{ minRows: 5, maxRows: 5 }"
        :placeholder="t('home_module.please_enter_a_question')"
        @keydown="handleQuestionSubmitEnter"
      >
        <template #suffix>
          <div class="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>

    <div class="mt-[20px] 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(.application-select-btn) {
  .n-button__border {
    border-color: #9ea3ff;
  }
}

.application-select-menu-enter-active,
.application-select-menu-leave-active {
  transition-timing-function: ease-in-out;
  transition-duration: 0.2s;
  transition-property: opacity, scale;
  transform-origin: left center;
}

.application-select-menu-enter-from,
.application-select-menu-leave-to {
  opacity: 0;
  scale: 0.8;
}

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

.file-upload-enter-from,
.file-upload-leave-to {
  opacity: 0;
}
</style>
