<template>
  <o-base-section ref="scrollRef"
                    class="chat-conversation"
                    :class="{ 'start': start }"
                    @scroll="onScroll"
                  scrollable>
    <section class="row col-12 justify-center">
      <section class="row justify-center start-panel" v-if="start">
        <header>
          <div class="row justify-center items-center welcome">
            <img :src="$public('/logo.png')" alt="Logo" />
            {{ $t('chat.assistant.welcome') }}
          </div>
          <div class="message text-readable">{{ $t('chat.assistant.tips') }}</div>
        </header>
        <footer></footer>
      </section>
      <section class="row col-12 justify-center" v-else>
        <section class="chat-list">
          <template v-for="(item, index) in chats" :key="index">
            <o-chat-message :chat="item"
                            align-right
                            note
                            @favorite="onFavorite($event, index)" />
          </template>

          <template v-if="isLoading">
            <o-chat-message :chat="newChat"
                            align-right
                            note
                            :streaming="isLoading" />
          </template>
        </section>
      </section>
      <section class="row col-12 justify-center q-pb-lg new-chat" v-show="chats.length">
        <q-btn class="bg-primary text-white"
               flat
               @click="onNewChat" v-intersection="onIntersection">
          <q-icon name="add_comment" class="flip-horizontal" />
          <span class="q-ml-sm">{{ $t('chat.conversation.new') }}</span>
        </q-btn>
      </section>

      <transition name="fade">
        <section class="q-pb-lg scroll-bottom" v-if="chats.length && showScrollBtn">
          <div class="row col-12 justify-end btn-wrapper">
            <q-btn icon="south" class="bg-dark text-info" flat round @click="scrollToBottom(500, true)" />
          </div>
        </section>
      </transition>
    </section>

    <o-chat-toc ref="tocRef" :chats="chats" />
  </o-base-section>
</template>

<script setup lang="ts">
import { computed, ref, onMounted, nextTick, onActivated } from 'vue'
import { useRoute } from 'vue-router'
import OBaseSection from 'core/page/section/OBaseSection.vue'
import OChatMessage from 'components/chat/OChatMessage.vue'
import OChatToc from 'components/chat/OChatToc.vue'

import { router } from 'src/router'
import { chatService } from 'src/api/service/remote/chat'
import { chatConversationService } from 'src/api/service/remote/chat-conversation'
import useAi from 'src/hooks/useAi'
import useStream from 'src/hooks/useStream'
import useChatConversation from 'src/hooks/useChatConversation'
import type { ChatInput } from 'src/types/chat'

const route = useRoute()
const {
  appId,
  conversation,
  conversationId,
  chatStore,
  getConversation,
} = useChatConversation()
const { isLoading, startStream, cancelStream } = useStream()

const scrollRef = ref<InstanceType<typeof OBaseSection>>()
const start = ref(true)
const chats = ref<Indexable[]>([])
const newChat = ref<Indexable>({})
const showScrollBtn = ref(false)
const tocRef = ref<InstanceType<typeof OChatToc>>()
const scrollable = ref(true)

async function init() {
  start.value = route.name === 'chat-start'
  appId.value = (route.params.appId || '') as string
  conversationId.value = (route.params.id || '') as string
  if (conversationId.value) {
    await getConversation()
    getAllChats()

    // Resend after replace router
    const chatId = (route.query.id || '') as string
    const currentChat = chatStore.value.getChat(chatId)
    if (currentChat) {
      onSend(currentChat, true)
    }
    router.replace({...route, query: {} })
  }
}

function getAllChats() {
  chatService.getMessages(conversationId.value).then(res => {
    chats.value = res as Indexable[]
    scrollToBottom()
  })
}

async function onSend(data: ChatInput, reset = false) {
  newChat.value = data
  scrollToBottom(0, true)

  if (reset) {
    chatStore.value.removeChat(data.id)
  } else {
    chatStore.value.addChat(data)
  }

  if (conversationId.value) {
    chatCompletion(data)
  } else {
    start.value = false
    createConversation(data)
  }
}

function onStop() {
  cancelStream()
}

async function createConversation(data: ChatInput) {
  const { message, modelProvider, modelType, modelName } = data
  chatConversationService.save({
    appId: appId.value || null,
    name: message,
    modelProvider,
    modelName,
    modelType
  }).then(res => {
    conversation.value = res
    conversationId.value = res.id
    start.value = false

    // update conversation list
    chatStore.value.setSessionTimer(Date.now())

    // replace router
    router.replace({
      name: 'chat-conversation',
      params: { appId: res.appId, id: res.id },
      query: { id: data.id }
    })
  })
}

async function chatCompletion(data: ChatInput) {
  chatStore.value.removeChat(data.id)
  const payload = {
    ...data,
    conversationId: conversationId.value,
    stream: true,
  }
  newChat.value = {
    ...payload,
    modelProvider: conversation.value?.modelProvider,
    modelType: conversation.value?.modelType,
    modelName: conversation.value?.modelName,
  }

  await startStream({
    url: '/chat/completions',
    payload,
    onProgress,
    onDone,
    onErrorDone
  })
}

async function onProgress(reasoningText: string, text: string) {
  newChat.value.content = text
  newChat.value.reasoningContent = reasoningText
  scrollToBottom()
}

async function onDone(reasoningText: string, text: string) {
  newChat.value.content = text
  newChat.value.reasoningContent = reasoningText
  chats.value.push({...newChat.value})
  newChat.value = {}
  scrollToBottom()
}

async function onErrorDone(chat: Indexable) {
  newChat.value = chat
  chats.value.push({...newChat.value})
  newChat.value = {}
  scrollToBottom()
}

function onFavorite(item: Indexable, index: number) {
  chats.value.splice(index, 1, item)
}

function onNewChat() {
  router.replace({name: 'chat-start'})
}

async function scrollToBottom(duration = 0, manual = false) {
  if (!scrollable.value && !manual) return
  await nextTick()
  setTimeout(() => {
    scrollRef.value?.scrollToBottom(duration)
  }, 0)
}

function onScroll(info: Indexable, direction: string) {
  tocRef.value?.onScroll()
  if (direction === 'up') {
    scrollable.value = false
  }
}

function onIntersection(entry: Indexable) {
  showScrollBtn.value = !entry.isIntersecting
  if (entry.isIntersecting) {
    scrollable.value = true
  }
  // console.log('inter', scrollable.value);
}

onActivated(() => {
  init()
})

defineExpose({
  isLoading,
  onSend,
  onStop
})
</script>

<style lang="scss">
.chat-conversation {
  &.start {
    padding: 0 1rem;
    .q-scrollarea__content {
      display: flex;
      justify-content: center;
      align-items: center;
    }
  }

  .o-page-container {
    top: 0;
    bottom: 140px;
    padding: 0 1rem 0 1rem;
  }

  .chat-list {
    width: 100%;
    max-width: 800px;
    padding: 1rem;
  }

  .start-panel {
    width: 100%;
    max-width: 800px;

    header, footer, .start-card {
      width: 100%;
    }

    header {
      text-align: center;
      padding-bottom: 20px;
      .welcome {
        font-size: 1.6rem;
        font-weight: 600;

        img {
          display: block;
          height: 32px;
          margin-right: 8px;
        }
      }
      .message {
        margin-top: 4px;
      }
    }

    footer {
      height: 200px;
    }
  }

  .new-chat {
    .q-btn {
      border-radius: 10px;
    }
  }

  .scroll-bottom {
    position: fixed;
    right: 1rem;
    bottom: -4px;
    .btn-wrapper {
      width: 42px;
      max-width: 800px;
    }
  }

  .o-chat-toc {
    position: fixed;
    top: 1rem;
    right: 1rem;
  }
}
</style>
