<script setup lang="ts">
import type { AiChatApi } from '#/api/core/ai-chat';

import { computed, nextTick, onMounted, ref } from 'vue';

import {
  Avatar,
  Button,
  Input,
  message,
  Modal,
  Popconfirm,
  Spin,
} from 'ant-design-vue';

import MarkdownIt from 'markdown-it';
import hljs from 'highlight.js';

import {
  createConversationApi,
  deleteConversationApi,
  getConversationDetailApi,
  getConversationsApi,
  streamMessageApi,
} from '#/api/core/ai-chat';

const md = new MarkdownIt({
  html: false,
  linkify: true,
  breaks: true,
  highlight(str: string, lang: string) {
    if (lang && hljs.getLanguage(lang)) {
      try {
        return `<pre class="hljs"><code>${hljs.highlight(str, { language: lang }).value}</code></pre>`;
      } catch {
        // fallback
      }
    }
    return `<pre class="hljs"><code>${md.utils.escapeHtml(str)}</code></pre>`;
  },
});

const inputMessage = ref('');
const sending = ref(false);
const loading = ref(false);
const messageListRef = ref<HTMLElement | null>(null);
const showNewChat = ref(false);
const newChatTitle = ref('');

const conversations = ref<AiChatApi.Conversation[]>([]);
const activeId = ref<string | null>(null);
const messages = ref<AiChatApi.Message[]>([]);

const activeConversation = computed(() =>
  conversations.value.find((c) => c.id === activeId.value),
);

function formatTime(dateStr: string) {
  const date = new Date(dateStr);
  return date.toLocaleString('zh-CN', {
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  });
}

function renderMarkdown(text: string) {
  return md.render(text);
}

function scrollToBottom() {
  nextTick(() => {
    if (messageListRef.value) {
      messageListRef.value.scrollTop = messageListRef.value.scrollHeight;
    }
  });
}

async function loadConversations() {
  loading.value = true;
  try {
    conversations.value = await getConversationsApi();
  } catch {
    message.error('加载会话失败');
  } finally {
    loading.value = false;
  }
}

async function selectConversation(id: string) {
  activeId.value = id;
  messages.value = [];
  try {
    const detail = await getConversationDetailApi(id);
    messages.value = detail.messages;
    scrollToBottom();
  } catch {
    message.error('加载消息失败');
  }
}

async function handleCreateChat() {
  const title = newChatTitle.value.trim() || '新对话';
  try {
    const conv = await createConversationApi({ title });
    conversations.value.unshift(conv);
    activeId.value = conv.id;
    messages.value = [];
    showNewChat.value = false;
    newChatTitle.value = '';
  } catch {
    message.error('创建会话失败');
  }
}

async function handleDelete(id: string) {
  try {
    await deleteConversationApi(id);
    conversations.value = conversations.value.filter((c) => c.id !== id);
    if (activeId.value === id) {
      activeId.value = null;
      messages.value = [];
    }
    message.success('已删除');
  } catch {
    message.error('删除失败');
  }
}

async function handleSend(e?: any) {
  if (e?.shiftKey) return;
  e?.preventDefault?.();

  const content = inputMessage.value.trim();
  if (!content || !activeId.value) return;

  // 添加用户消息到 UI
  messages.value.push({
    id: `temp-user-${Date.now()}`,
    role: 'User',
    content,
    tokenUsage: null,
    createdAt: new Date().toISOString(),
  });
  inputMessage.value = '';
  scrollToBottom();

  // 添加 AI 消息占位
  const aiMsgId = `temp-ai-${Date.now()}`;
  messages.value.push({
    id: aiMsgId,
    role: 'Assistant',
    content: '',
    tokenUsage: null,
    createdAt: new Date().toISOString(),
  });
  scrollToBottom();

  sending.value = true;
  try {
    const stream = await streamMessageApi(activeId.value!, content);
    const reader = stream.getReader();
    const decoder = new TextDecoder();
    let aiContent = '';
    let buffer = '';

    // eslint-disable-next-line no-constant-condition
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });

      // 按双换行分割 SSE 事件
      const parts = buffer.split('\n\n');
      // 最后一段可能不完整，保留到下次处理
      buffer = parts.pop() ?? '';

      for (const part of parts) {
        const lines = part.split('\n');
        let dataLine = '';
        for (const line of lines) {
          if (line.startsWith('event: done')) {
            break;
          }
          if (line.startsWith('data: ')) {
            dataLine = line.slice(6).trim();
          }
        }
        if (!dataLine || dataLine === '[DONE]') continue;

        try {
          const parsed = JSON.parse(dataLine);
          if (parsed.content) {
            aiContent += parsed.content;
            const msg = messages.value.find((m) => m.id === aiMsgId);
            if (msg) msg.content = aiContent;
            scrollToBottom();
          }
        } catch {
          // skip malformed JSON
        }
      }
    }

    // 流式结束后从服务端重新拉取最新消息（走 Redis 缓存）
    const detail = await getConversationDetailApi(activeId.value!);
    messages.value = detail.messages;
    scrollToBottom();
  } catch {
    const msg = messages.value.find((m) => m.id === aiMsgId);
    if (msg) msg.content = '请求失败，请重试';
    message.error('发送失败');
  } finally {
    sending.value = false;
    scrollToBottom();
  }
}

onMounted(loadConversations);
</script>

<template>
  <div
    class="flex h-[calc(100vh-120px)] overflow-hidden rounded-lg border border-border bg-card"
  >
    <!-- Sidebar -->
    <div class="flex w-[280px] shrink-0 flex-col border-r border-border">
      <div class="flex items-center justify-between border-b border-border px-4 py-3">
        <span class="text-sm font-medium text-foreground">AI 对话</span>
        <Button type="primary" size="small" @click="showNewChat = true">
          新对话
        </Button>
      </div>
      <div class="flex-1 overflow-y-auto">
        <Spin :spinning="loading">
          <div
            v-for="conv in conversations"
            :key="conv.id"
            class="group flex cursor-pointer items-center justify-between px-4 py-3 transition-colors hover:bg-accent"
            :class="{
              'bg-primary/10 dark:bg-accent': conv.id === activeId,
            }"
            @click="selectConversation(conv.id)"
          >
            <div class="flex-1 overflow-hidden">
              <div class="truncate text-sm font-medium text-foreground">
                {{ conv.title }}
              </div>
              <div class="mt-1 truncate text-xs text-muted-foreground">
                {{ formatTime(conv.createdAt) }}
              </div>
            </div>
            <Popconfirm
              title="确定删除？"
              @confirm="handleDelete(conv.id)"
            >
              <Button
                type="text"
                size="small"
                danger
                class="invisible group-hover:visible"
                @click.stop
              >
                删除
              </Button>
            </Popconfirm>
          </div>
          <div
            v-if="!loading && conversations.length === 0"
            class="px-4 py-8 text-center text-sm text-muted-foreground"
          >
            暂无对话，点击上方按钮创建
          </div>
        </Spin>
      </div>
    </div>

    <!-- Main chat area -->
    <div class="flex flex-1 flex-col">
      <template v-if="activeConversation">
        <div
          class="flex items-center border-b border-border px-4 py-3 font-medium text-foreground"
        >
          <Avatar :size="32" class="mr-2 bg-blue-500">AI</Avatar>
          <span>{{ activeConversation.title }}</span>
        </div>

        <!-- Messages -->
        <div ref="messageListRef" class="flex-1 overflow-y-auto px-4 py-4">
          <div
            v-if="messages.length === 0"
            class="flex h-full items-center justify-center text-muted-foreground"
          >
            发送消息开始对话
          </div>
          <div
            v-for="msg in messages"
            :key="msg.id"
            class="mb-4"
            :class="{ 'text-right': msg.role === 'User' }"
          >
            <div class="mb-1 text-xs text-muted-foreground">
              {{ msg.role === 'User' ? '你' : 'AI 助手' }}
            </div>
            <div
              class="inline-block max-w-[70%] break-words rounded-lg px-3 py-2 text-left"
              :class="
                msg.role === 'User'
                  ? 'bg-primary text-primary-foreground whitespace-pre-wrap'
                  : 'bg-muted text-foreground'
              "
            >
              <Spin v-if="!msg.content" size="small" />
              <template v-else-if="msg.role === 'User'">{{ msg.content }}</template>
              <!-- eslint-disable-next-line vue/no-v-html -->
              <div v-else class="markdown-body" v-html="renderMarkdown(msg.content)" />
            </div>
          </div>
        </div>

        <!-- Input -->
        <div class="border-t border-border px-3 py-2">
          <div class="flex gap-2">
            <textarea
              v-model="inputMessage"
              :disabled="sending"
              class="flex-1 resize-none rounded-md border border-border bg-transparent px-3 py-1.5 text-sm outline-none focus:border-primary"
              placeholder="输入消息... (Shift+Enter 换行)"
              :rows="1"
              @keydown.enter="handleSend"
            />
            <Button type="primary" :loading="sending" @click="handleSend">
              发送
            </Button>
          </div>
        </div>
      </template>

      <div
        v-else
        class="flex flex-1 flex-col items-center justify-center gap-4 text-muted-foreground"
      >
        <div class="text-5xl">🤖</div>
        <p>选择或创建一个对话开始</p>
      </div>
    </div>

    <!-- New Chat Modal -->
    <Modal
      v-model:open="showNewChat"
      title="新建对话"
      @ok="handleCreateChat"
    >
      <Input
        v-model:value="newChatTitle"
        placeholder="对话标题（可选）"
        @keydown.enter="handleCreateChat"
      />
    </Modal>
  </div>
</template>

<style scoped>
.markdown-body {
  font-size: 14px;
  line-height: 1.6;
  word-wrap: break-word;
}

.markdown-body :deep(p) {
  margin: 0 0 8px;
}

.markdown-body :deep(p:last-child) {
  margin-bottom: 0;
}

.markdown-body :deep(pre) {
  margin: 8px 0;
  padding: 12px;
  border-radius: 6px;
  overflow-x: auto;
  background: #1e1e2e;
}

.markdown-body :deep(code) {
  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
  font-size: 13px;
}

.markdown-body :deep(:not(pre) > code) {
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.1);
  font-size: 0.9em;
}

.markdown-body :deep(ul),
.markdown-body :deep(ol) {
  margin: 8px 0;
  padding-left: 20px;
}

.markdown-body :deep(blockquote) {
  margin: 8px 0;
  padding-left: 12px;
  border-left: 3px solid rgba(0, 0, 0, 0.15);
  color: rgba(0, 0, 0, 0.6);
}

.markdown-body :deep(table) {
  border-collapse: collapse;
  margin: 8px 0;
  width: 100%;
}

.markdown-body :deep(th),
.markdown-body :deep(td) {
  border: 1px solid rgba(0, 0, 0, 0.15);
  padding: 6px 12px;
  text-align: left;
}

.markdown-body :deep(th) {
  background: rgba(0, 0, 0, 0.05);
}
</style>
