<!--
  - Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
  -
  - Licensed under the GNU Affero General Public License, Version 3 (the "License");
  - you may not use this file except in compliance with the License.
  - You may obtain a copy of the License at
  -
  -     https://www.gnu.org/licenses/agpl-3.0.html
  -
  - Unless required by applicable law or agreed to in writing, software
  - distributed under the License is distributed on an "AS IS" BASIS,
  - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  - See the License for the specific language governing permissions and
  - limitations under the License.
  -->

<script lang="ts" setup>
import Message from '@/views/ai/chat/new-chat/message/Message.vue'
import { useProjectSetting } from '@/hooks/setting/useProjectSetting'
import { computed, ref } from 'vue'
import { v4 as uuidv4 } from 'uuid'
import { useChatStore } from './store/useChatStore'
import { useScroll } from './store/useScroll'
import { ElMessage, ElMessageBox } from 'element-plus'
import { CircleClose, Position } from '@element-plus/icons-vue'
import { chat } from '@/api/new-ai/chat'

const ms = ElMessage
const chatStore = useChatStore()
const { scrollRef, contentRef, scrollToBottom, scrollToBottomIfAtBottom } = useScroll()
const { isMobile } = useProjectSetting()
const loading = ref<boolean>(false)
const message = ref('')
const chatId = ref<string>('')
const aiChatId = ref<string>('')
let controller = new AbortController()

const footerClass = computed(() => {
  let classes = ['p-4']
  if (isMobile.value) {
    classes = ['sticky', 'left-0', 'bottom-0', 'right-0', 'p-2', 'pr-3', 'overflow-hidden']
  }
  return classes
})

const dataSources = computed(() => {
  scrollToBottom()
  return chatStore.messages
})

function handleEnter(event: KeyboardEvent) {
  if (!isMobile.value) {
    if (event.key === 'Enter' && !event.shiftKey) {
      event.preventDefault()
      handleSubmit()
    }
  } else {
    if (event.key === 'Enter' && event.ctrlKey) {
      event.preventDefault()
      handleSubmit()
    }
  }
}

async function handleSubmit() {
  const msg = message.value
  if (loading.value) {
    return
  }
  if (!msg || msg.trim() === '') {
    ms.error('请先输入消息内容')
    return
  }
  controller = new AbortController()

  // user
  chatId.value = uuidv4()
  await chatStore.addMessage(msg, 'user', chatId.value)

  loading.value = true
  message.value = ''

  // ai
  await scrollToBottom()
  aiChatId.value = uuidv4()
  await scrollToBottom()
  await chatStore.addMessage('', 'assistant', aiChatId.value)
  await scrollToBottomIfAtBottom()

  await onChat(msg)
}

async function onChat(message: string) {
  try {
    await chat(
      {
        chatId: chatId.value,
        conversationId: chatStore.conversationId,
        appId: chatStore.appId,
        message,
        role: 'user',
        modelId: chatStore.modelId,
        modelName: chatStore.modelName,
        modelProvider: chatStore.modelProvider,
      },
      controller,
      async ({ event }) => {
        const list = event.target.responseText.split('\n\n')

        let text = ''
        let isRun = true
        list.forEach((i: any) => {
          if (i.startsWith('data:Error')) {
            isRun = false
            text += i.substring(5, i.length)
            chatStore.updateMessage(aiChatId.value, text, true)
            return
          }
          if (!i.startsWith('data:{')) {
            return
          }

          const { done, message } = JSON.parse(i.substring(5, i.length))
          if (done || message === null) {
            return
          }
          text += message
        })
        if (!isRun) {
          await scrollToBottomIfAtBottom()
          return
        }
        await chatStore.updateMessage(aiChatId.value, text, false)
        await scrollToBottomIfAtBottom()
      }
    )
      .catch((e: any) => {
        loading.value = false
        console.error('chat error', e)
        if (e.message !== undefined) {
          chatStore.updateMessage(aiChatId.value, e.message || 'chat error', true)
          return
        }
        if (e.startsWith('data:Error')) {
          chatStore.updateMessage(aiChatId.value, e.substring(5, e.length), true)
          return
        }
      })
      .finally(() => {
        scrollToBottomIfAtBottom()
      })
  } finally {
    loading.value = false
  }
}

function handleStop() {
  if (loading.value) {
    controller.abort()
    controller = new AbortController()
    loading.value = false
  }
}

function handleDelete(item: any) {
  if (loading.value) {
    return
  }

  ElMessageBox.confirm('确认删除消息', '删除消息', {
    confirmButtonText: '是',
    cancelButtonText: '否',
    type: 'warning',
  })
    .then(() => {
      chatStore.delMessage(item)
    })
    .catch(() => {})
}
</script>

<template>
  <div class="flex flex-col w-full h-full">
    <main class="flex-1 overflow-hidden">
      <div ref="contentRef" class="h-full overflow-hidden overflow-y-auto">
        <div
          ref="scrollRef"
          :class="[isMobile ? 'p-2' : 'p-5']"
          class="w-full max-w-screen-3xl m-auto"
        >
          <Message
            v-for="(item, index) of dataSources"
            :key="index"
            :class="dataSources.length - 1 == index ? '!mb-2' : 'mb-6'"
            :date-time="item.createTime"
            :error="item.isError"
            :inversion="item.role !== 'assistant'"
            :loading="loading"
            :text="item.message"
            @delete="handleDelete(item)"
          />
        </div>
      </div>
    </main>

    <footer :class="footerClass">
      <div class="w-full max-w-screen-3xl m-auto">
        <div class="flex items-center justify-between space-x-2">
          <el-input
            v-model="message"
            :autosize="{ minRows: 1, maxRows: isMobile ? 4 : 8 }"
            :placeholder="loading ? '等待响应中...' : '请输入消息内容'"
            :disabled="loading"
            type="textarea"
            @keypress="handleEnter"
          />
          <div class="flex items-center space-x-1">
            <el-button v-if="loading" type="danger" @click="handleStop">
              <template #icon>
                <el-icon><CircleClose /></el-icon>
              </template>
              停止
            </el-button>
            <el-button v-else type="primary" @click="handleSubmit">
              <template #icon>
                <el-icon><Position /></el-icon>
              </template>
              发送
            </el-button>
          </div>
        </div>
      </div>
    </footer>
  </div>
</template>

<style lang="scss" scoped>
:deep(.el-textarea__inner) {
  padding: 8px 12px;
  min-height: 42px;
  max-height: 200px;
  resize: none;
}
</style>
