<script setup lang="ts">
import { onMounted, ref, useTemplateRef, watch } from 'vue'
import AgentAbout from './components/agent-about.vue'
import FooterOperation from './components/footer-operation.vue'
import HistoryMenuSidebar from './components/history-menu-sidebar.vue'
import MessageList from './components/message-list.vue'
import type { AgentApplicationRecordItem, MessageItemInterface } from './types'
import { fetchCreateSessionId, fetchMessageRecordList } from '@/apis/home-agent'
import type { ValueOf } from 'type-fest'
import { useElementSize } from '@vueuse/core'
import { debounce } from 'lodash-es'
import { nanoid } from 'nanoid'
import { useI18n } from 'vue-i18n'

const { t } = useI18n()

const homeContainerRef = useTemplateRef<HTMLDivElement>('homeContainerRef')
const messageListRef = useTemplateRef<InstanceType<typeof MessageList>>('messageListRef')
const historyMenuSidebarRef = useTemplateRef<InstanceType<typeof HistoryMenuSidebar>>('historyMenuSidebarRef')
const footerOperationRef = useTemplateRef<InstanceType<typeof FooterOperation>>('footerOperationRef')

const { width: homeContainerWidth } = useElementSize(homeContainerRef)

const isShowHistoryMenu = ref(true)

const currentAgentApplication = ref<AgentApplicationRecordItem>({
  agentId: '',
  agentTitle: '',
  agentAvatar: '',
  agentDesc: '',
  creator: '',
  publishedTime: '',
  points: 0,
  isDocumentParsing: 'N',
  unitIds: [],
})
const currentSessionId = ref('')

const questionContent = ref('')

const messageList = ref(new Map<string, MessageItemInterface>())
const isAgentResponding = ref(false)
const isShowMessageList = ref(false)
const isAgentInitLoading = ref(true)
const currentFetchEventSourceController = ref<AbortController | null>(null)

// messageList.value.set('1', {
//   role: 'user',
//   agentId: 'b058f1baedd04af983ca00775368bb8c',
//   content: '请推荐一些适合初学者的编程学习资源。',
//   timestamp: 1726654820427,
//   isAnswerLoading: true,
// })
// messageList.value.set('2', {
//   role: 'assistant',
//   agentId: 'b058f1baedd04af983ca00775368bb8c',
//   content:
//     '对于初学者的编程学习资源，可以从以下几个方面进行推荐：\n\n### 一、在线教程与网站\n\n1. **w3school**：这是一个非常全面的编程学习网站，提供了从基础到高级的教程，包括HTML、CSS、JavaScript、SQL等，适合初学者逐步深入学习。\n2. **慕课网**：慕课网上有许多免费课程，涵盖了前端、后端开发，移动开发等多个方面，初学者可以根据自己的兴趣选择相应的课程。\n3. **Coursera**：该网站提供世界名校的网络公开课程，其中也包括计算机编程的相关课程，初学者可以接触到国际一流的教学资源。\n\n### 二、书籍推荐\n\n1. **《Python编程快速上手》**：这本书是为零基础读者打造的Python入门书籍，内容系统且详细，每个知识点都深入浅出，非常适合初学者。\n2. **《C++ Primer Plus》**：这本书是C++语言学习的理想图书，通过大量短小精悍的程序详细阐述了C++的基本概念和技术，对初学者极为友好。\n\n### 三、实践项目与刷题网站\n\n1. **Stack Overflow**：这是一个程序设计领域的问答网站，初学者在遇到编程难题时可以在这里寻找解决方案，同时也可以学习到其他技术大牛的经验和技巧。\n2. **GitHub**：作为全球最大的开源代码托管仓库，GitHub上有无穷无尽的开源代码供初学者学习和参考，阅读源码是一个快速提升编程能力的好方法。\n\n### 四、社区与论坛\n\n1. **CSDN软件开发网**：这是国内知名的软件开发社区，提供了大量的编程资源和经验分享，初学者可以在这里交流学习心得，获取最新的技术动态。\n\n综上所述，初学者可以根据自己的学习需求和兴趣选择合适的编程学习资源。从在线教程、书籍阅读到实践项目和社区交流，多方面的学习将有助于初学者快速掌握编程技能并不断提升自己。',
//   timestamp: 1726654851735,
//   isAnswerLoading: false,
//   avatar: 'http://localhost:8848/fe/src/assets/images/home/agent-avatar.png',
//   name: 'lisa',
//   reasoningContent:
//     '对于初学者的编程学习资源，可以从以下几个方面进行推荐：\n\n### 一、在线教程与网站\n\n1. **w3school**：这是一个非常全面的编程学习网站，提供了从基础到高级的教程，包括HTML、CSS、JavaScript、SQL等，适合初学者逐步深入学习。\n2. **慕课网**：慕课网上有许多免费课程，涵盖了前端、后端开发，移动开发等多个方面，初学者可以根据自己的兴趣选择相应的课程。\n3. **Coursera**：该网站提供世界名校的网络公开课程，其中也包括计算机编程的相关课程，初学者可以接触到国际一流的教学资源。\n\n### 二、书籍推荐\n\n1. **《Python编程快速上手》**：这本书是为零基础读者打造的Python入门书籍，内容系统且详细，每个知识点都深入浅出，非常适合初学者。\n2. **《C++ Primer Plus》**：这本书是C++语言学习的理想图书，通过大量短小精悍的程序详细阐述了C++的基本概念和技术，对初学者极为友好。\n\n### 三、实践项目与刷题网站\n\n1. **Stack Overflow**：这是一个程序设计领域的问答网站，初学者在遇到编程难题时可以在这里寻找解决方案，同时也可以学习到其他技术大牛的经验和技巧。\n2. **GitHub**：作为全球最大的开源代码托管仓库，GitHub上有无穷无尽的开源代码供初学者学习和参考，阅读源码是一个快速提升编程能力的好方法。\n\n### 四、社区与论坛\n\n1. **CSDN软件开发网**：这是国内知名的软件开发社区，提供了大量的编程资源和经验分享，初学者可以在这里交流学习心得，获取最新的技术动态。\n\n综上所述，初学者可以根据自己的学习需求和兴趣选择合适的编程学习资源。从在线教程、书籍阅读到实践项目和社区交流，多方面的学习将有助于初学者快速掌握编程技能并不断提升自己',
// })

const homeContainerWidthWatchDebounce = debounce((newWidth) => {
  if (newWidth <= 1120) {
    isShowHistoryMenu.value = false
  } else if (newWidth >= 1320) {
    isShowHistoryMenu.value = true
  }
}, 300)

watch(homeContainerWidth, homeContainerWidthWatchDebounce)

watch(
  () => messageList.value.size,
  (newSize) => {
    if (newSize > 0) {
      setTimeout(() => {
        isShowMessageList.value = true
      }, 400)
    } else if (newSize === 0 && isShowMessageList.value) {
      setTimeout(() => {
        isShowMessageList.value = false
      }, 400)
    }
  },
)
;(function () {
  createSessionId()
})()

onMounted(() => {
  setTimeout(() => {
    messageListScrollToBottom()
  }, 700)
})

function createSessionId() {
  fetchCreateSessionId<string>().then((res) => {
    currentSessionId.value = res.data

    isAgentInitLoading.value = false
  })
}

function onCreateNewSession() {
  if (messageList.value.size === 0) {
    return
  }

  createSessionId()

  messageList.value.clear()
}

function messageListScrollToBottom() {
  if (messageListRef.value) {
    messageListRef.value.scrollToBottom()
  }
}

function onAddMessageItem(messageId: string, messageItem: MessageItemInterface) {
  messageList.value.set(messageId, messageItem)
}

function onUpdateSpecifyMessageItem(messageId: string, newMessageItem: Partial<MessageItemInterface>) {
  const currentMessageItemInfo = messageList.value.get(messageId)

  if (currentMessageItemInfo) {
    const updatePropertyLength = Object.keys(newMessageItem).length

    if (updatePropertyLength > 4) {
      messageList.value.set(messageId, Object.assign({}, currentMessageItemInfo, newMessageItem))
      return
    }

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

function onDeleteMessageItem(messageId: string) {
  messageList.value.delete(messageId)
}

function onHistoryRecordListUpdate() {
  if (historyMenuSidebarRef.value) {
    historyMenuSidebarRef.value.historyRecordListUpdate()
  }
}

function onGetMessageRecordList(recordId: string) {
  currentSessionId.value = recordId

  const loadingCtl = window.$message.loading(`${t('home_module.switching_over')}...`)

  fetchMessageRecordList<
    {
      role: 'user' | 'assistant'
      agentId: string
      agentAvatar: string
      agentTitle: string
      content: string
      timestamp: number
      imageUrl: string
      reasoningContent: string
    }[]
  >(recordId)
    .then((res) => {
      if (res.data && Array.isArray(res.data)) {
        const messageListDraft: (string | MessageItemInterface)[][] = res.data.map((recordItem) => {
          return [
            nanoid(),
            {
              role: recordItem.role,
              agentId: recordItem.agentId,
              content: recordItem.content,
              isAnswerLoading: false,
              name: recordItem.agentTitle,
              avatar: recordItem.agentAvatar,
              timestamp: recordItem.timestamp,
              imageUrl: recordItem?.imageUrl || '',
              reasoningContent: recordItem.reasoningContent || '',
            },
          ]
        })

        messageList.value = new Map(messageListDraft as any)

        setTimeout(() => {
          messageListScrollToBottom()
        }, 500)
      }
    })
    .catch(() => {
      window.$message.error(t('home_module.history_application_failed_please_try_again'))
    })
    .finally(() => {
      loadingCtl.destroy()
    })
}

function onClearSessionReferenceFile() {
  footerOperationRef.value && footerOperationRef.value.clearSessionReferenceFile()
}
</script>

<template>
  <div ref="homeContainerRef" class="relative h-full min-h-[650px] w-full">
    <div
      class="bg-px-home-home_bg-png relative h-full w-full bg-contain bg-center bg-no-repeat pr-0 transition-[padding] duration-300 ease-in-out"
      :class="{ '!pr-[293px]': isShowHistoryMenu }"
    >
      <div class="flex h-full flex-col px-[5px] pb-[14px]">
        <AgentAbout
          v-model:question-content="questionContent"
          :message-list-length="messageList.size"
          :current-agent-application="currentAgentApplication"
        />

        <MessageList v-show="isShowMessageList" ref="messageListRef" :message-list="messageList" />

        <FooterOperation
          ref="footerOperationRef"
          v-model:current-agent-application="currentAgentApplication"
          v-model:is-agent-responding="isAgentResponding"
          v-model:question-content="questionContent"
          v-model:current-fetch-event-source-controller="currentFetchEventSourceController"
          :current-session-id="currentSessionId"
          :message-list-length="messageList.size"
          @message-list-scroll-to-bottom="messageListScrollToBottom"
          @add-message-item="onAddMessageItem"
          @update-specify-message-item="onUpdateSpecifyMessageItem"
          @delete-message-item="onDeleteMessageItem"
          @create-new-session="onCreateNewSession"
          @history-record-list-update="onHistoryRecordListUpdate"
        />
      </div>

      <HistoryMenuSidebar
        ref="historyMenuSidebarRef"
        v-model="isShowHistoryMenu"
        v-model:is-agent-responding="isAgentResponding"
        :current-fetch-event-source-controller="currentFetchEventSourceController"
        :current-session-id="currentSessionId"
        @get-message-record-list="onGetMessageRecordList"
        @create-new-session="onCreateNewSession"
        @history-record-list-update="onHistoryRecordListUpdate"
        @clear-session-reference-file="onClearSessionReferenceFile"
      />
    </div>

    <Transition name="mask" mode="out-in">
      <div
        v-show="isAgentInitLoading"
        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>

<style lang="scss" scoped>
.mask-enter-active,
.mask-leave-active {
  transition-timing-function: ease-in-out;
  transition-duration: 0.2s;
  transition-property: opacity;
}

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