<script lang="ts" setup>
import ChatItem from "~/components/aigc/chat/ChatItem.vue";
import type {ChatMessage, ChatMessageId, ChatSessionId} from "~/components/aigc/chat";
import Message from "~/components/aigc/chat/Message.vue";
import {useIdGenerator} from "~/composables/useIdGenerator";
import {useHistory} from "~/composables/useHistory";

useHead({
  title: '聊天 | XSH AI'
})

const dayjs = useDayjs()
const toast = useToast()
const {generateUUID} = useIdGenerator()
const historyStore = useHistory()
const {chatSessions} = storeToRefs(historyStore)
const {setChatSessions} = historyStore

// const chatSessions = ref<ChatSession[]>([])
const currentSessionId = ref<ChatSessionId | null>(null)
const messagesWrapperRef = ref<HTMLDivElement | null>(null)

const user_input = ref('')
const responding = ref(false)

const getSessionCopyById = (chatSessionId: ChatSessionId) => chatSessions.value.find(s => s.id === chatSessionId);
const selectCurrentSessionId = (chatSessionId?: ChatSessionId) => {
  if (chatSessions.value.length > 0) {
    if (chatSessionId) { // 切换到指定 ID
      // 保存当前输入并清空输入框
      setChatSessions(chatSessions.value.map(s => s.id === currentSessionId.value ? {
        ...s,
        last_input: user_input.value,
      } : s))
      user_input.value = ''
      // 切换到指定 ID 会话
      currentSessionId.value = chatSessionId
      // 恢复输入
      user_input.value = getSessionCopyById(chatSessionId)?.last_input || ''
      // 清除已恢复的输入
      setChatSessions(chatSessions.value.map(s => s.id === chatSessionId ? {
        ...s,
        last_input: '',
      } : s))
    } else { // 切换到第一个会话
      currentSessionId.value = chatSessions.value[0].id
    }
  } else {
    handleClickCreateSession()
  }
}

const handleClickCreateSession = () => {
  // 生成一个新的会话 ID
  const sessionId = generateUUID()
  // 插入新会话数据
  setChatSessions([
    {
      id: sessionId,
      subject: '新对话',
      messages: [],
      create_at: dayjs().unix(),
    },
    ...chatSessions.value,
  ])
  // 切换到新的会话
  selectCurrentSessionId(sessionId)
}

const handleClickSend = (event: any) => {
  if (event.ctrlKey) {
    return;
  }
  if (responding.value) return;
  if (!user_input.value) return
  if (!currentSessionId.value) {
    toast.add({
      title: '发送失败',
      description: '请先选择一个会话',
      color: 'red',
      icon: 'i-tabler-circle-x',
    })
    return;
  }
  // 插入用户消息
  insetMessage({
    id: generateUUID(),
    role: 'user',
    content: user_input.value,
    create_at: dayjs().unix(),
  })
  user_input.value = ''
  // 进入响应中状态
  responding.value = true
  // 插入空助手消息(加载状态)
  const assistantReplyId = insetMessage({
    id: generateUUID(),
    role: 'assistant',
    content: '',
    create_at: dayjs().unix(),
  })
  // mock
  setTimeout(() => {
    modifyMessageContent(assistantReplyId, '草，走，忽略！ጿ ኈ ቼ ዽ ጿ')
    responding.value = false
  }, 1000)
}

const scrollToMessageListBottom = () => {
  nextTick(() => {
    messagesWrapperRef.value?.scrollTo({
      top: messagesWrapperRef.value.scrollHeight,
      behavior: 'smooth',
    })
  })
}

const insetMessage = (message: ChatMessage): ChatMessageId => {
  setChatSessions(chatSessions.value.map(s => s.id === currentSessionId.value ? {
    ...s,
    messages: [
      ...s.messages,
      message
    ]
  } : s))
  scrollToMessageListBottom()
  return message.id
}

const modifyMessageContent = (messageId: ChatMessageId, content: string) => {
  setChatSessions(chatSessions.value.map(s => s.id === currentSessionId.value ? {
    ...s,
    messages: s.messages.map(m => m.id === messageId ? {
      ...m,
      content,
    } : m)
  } : s))
  scrollToMessageListBottom()
}

onMounted(() => {
  // 切换到第一个会话, 没有会话会自动创建
  selectCurrentSessionId()
})
</script>

<template>
  <div class="w-full flex relative">
    <div class="h-[calc(100vh-4rem)] bg-neutral-100 dark:bg-neutral-900 p-4 flex flex-col w-[300px]
                shadow-sidebar border-r border-transparent dark:border-neutral-700">
      <div class="flex-1 flex flex-col overflow-auto overflow-x-hidden">
        <!-- list -->
        <div class="flex flex-col gap-3">
          <!-- ClientOnly avoids hydrate exception -->
          <ClientOnly>
            <ChatItem
                v-for="(session, i) in chatSessions"
                :chat-session="session" :key="i"
                :active="session.id === currentSessionId"
                @click="selectCurrentSessionId(session.id)"
                @remove="() => {
                  chatSessions.splice(chatSessions.findIndex(s => s.id === session.id), 1)
                  session.id === currentSessionId && selectCurrentSessionId()
                }"
            />
          </ClientOnly>
        </div>
      </div>
      <div class="pt-4 flex justify-between items-center">
        <div></div>
        <div>
          <UButton
              color="white"
              variant="solid"
              icon="i-tabler-message-circle-plus"
              @click="handleClickCreateSession"
          >
            新建聊天
          </UButton>
        </div>
      </div>
    </div>
    <div class="h-[calc(100vh-4rem)] bg-white dark:bg-neutral-900 flex-1 flex flex-col">
      <div class="w-full p-4 bg-neutral-50 dark:bg-neutral-800/50 border-b dark:border-neutral-700/50">
        {{ getSessionCopyById(currentSessionId!)?.subject || '新对话' }}
      </div>
      <div ref="messagesWrapperRef" class="flex-1 flex flex-col overflow-auto overflow-x-hidden">
        <div class="flex flex-col gap-8 px-4 py-8">
          <TransitionGroup name="message">
            <Message
                v-for="message in getSessionCopyById(currentSessionId!)?.messages || []"
                :message="message"
                :key="message.id"
            />
          </TransitionGroup>
        </div>
      </div>
      <div
          class="w-full p-4 pt-2 flex flex-col gap-2 bg-neutral-50 dark:bg-neutral-800/50 border-t dark:border-neutral-700/50">
        <div>action bar</div>
        <div class="relative">
          <UTextarea
              v-model="user_input"
              size="lg"
              autoresize
              :rows="5"
              :maxrows="12"
              class="font-sans"
              placeholder="Enter 发送, Ctrl + Enter 换行"
              @keydown.ctrl.enter="user_input += '\n'"
              @keydown.enter.prevent="handleClickSend"
          />
          <UButton
              color="black"
              variant="solid"
              icon="i-tabler-send-2"
              class="absolute bottom-2.5 right-3"
              @click.stop="handleClickSend"
          >
            发送
          </UButton>
        </div>
      </div>
    </div>
  </div>
</template>

<!--suppress CssUnusedSymbol -->
<style scoped>
.message-enter-active {
  @apply transition duration-300;
}

.message-enter-from {
  @apply translate-y-4 opacity-0;
}

.message-leave-to {
  @apply -translate-y-4 opacity-0;
}
</style>