<script setup lang="ts">
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
import { ChatDotRound, Select, Close } from '@element-plus/icons-vue'
import { useUserStore } from '@/store/user'
import { useChatStore } from '@/store/chat'
import { ElMessage } from 'element-plus'

const userStore = useUserStore()
const chatStore = useChatStore()

// 悬浮球状态
const isChatOpen = ref(false)

// 拖拽相关逻辑
const floatingBall = ref<HTMLElement | null>(null)
const position = ref({ x: document.documentElement.clientWidth - 80, y: document.documentElement.clientHeight - 80 })
let isDragging = false
let startX = 0
let startY = 0
let initialX = 0
let initialY = 0

const onMouseDown = (e: MouseEvent) => {
  if (e.button !== 0) return // Only left click
  isDragging = false // Reset drag state initially
  startX = e.clientX
  startY = e.clientY
  initialX = position.value.x
  initialY = position.value.y

  document.addEventListener('mousemove', onMouseMove)
  document.addEventListener('mouseup', onMouseUp)
}

const onMouseMove = (e: MouseEvent) => {
  const dx = e.clientX - startX
  const dy = e.clientY - startY
  
  // A small threshold to distinguish between click and drag
  if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
    isDragging = true
  }

  if (isDragging && floatingBall.value) {
    let newX = initialX + dx
    let newY = initialY + dy

    // Constraints to keep it within the viewport
    const ballWidth = 60
    const ballHeight = 60
    const maxX = document.documentElement.clientWidth - ballWidth
    const maxY = document.documentElement.clientHeight - ballHeight

    position.value.x = Math.max(0, Math.min(newX, maxX))
    position.value.y = Math.max(0, Math.min(newY, maxY))
  }
}

const onMouseUp = () => {
  document.removeEventListener('mousemove', onMouseMove)
  document.removeEventListener('mouseup', onMouseUp)
}

const toggleChat = (e: Event) => {
  if (!isDragging) {
    isChatOpen.value = !isChatOpen.value
  }
}

// ----------------

const inputMessage = ref('')
const isLoading = ref(false)
const chatBodyRef = ref<HTMLElement | null>(null)

const scrollToBottom = async () => {
  await nextTick()
  if (chatBodyRef.value) {
    chatBodyRef.value.scrollTop = chatBodyRef.value.scrollHeight
  }
}

const sendMessage = async () => {
  if (!inputMessage.value.trim() || isLoading.value) return

  const userMsg = inputMessage.value.trim()
  inputMessage.value = ''
  
  chatStore.appendMessage({
    id: Date.now().toString(),
    role: 'user',
    content: userMsg,
    type: 'text'
  })
  scrollToBottom()

  isLoading.value = true

  // Create an initial empty assistant message to append to
  const assistantMsgId = (Date.now() + 1).toString()
  chatStore.appendMessage({
    id: assistantMsgId,
    role: 'assistant',
    content: '',
    type: 'text' // Will be updated if an action_card arrives, or we handle it by appending new card objects
  })
  
  try {
    const token = userStore.token
    if (!token) {
        ElMessage.error('未登录或 Token 失效')
        isLoading.value = false
        return
    }

    const response = await fetch('/api/chat/stream', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${token}`
      },
      body: JSON.stringify({ message: userMsg, conversation_id: chatStore.conversationId })
    })

    if (!response.ok) {
        if(response.status === 401) {
            ElMessage.error('登录状态过期，请重新登录')
            userStore.logout()
            // Optional: redirect to login
        } else {
            ElMessage.error(`请求失败: ${response.status}`)
        }
        isLoading.value = false
        return
    }

    const reader = response.body?.getReader()
    const decoder = new TextDecoder('utf-8')

    if (!reader) {
        throw new Error("No reader available")
    }

    let buf = ''

    while (true) {
      const { done, value } = await reader.read()
      if (done) break

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

      // Process complete SSE messages separated by \n\n
      let idx: number
      while ((idx = buf.indexOf('\n\n')) >= 0) {
        const chunk = buf.slice(0, idx)
        buf = buf.slice(idx + 2)

        if (chunk.startsWith('data: ')) {
          const jsonStr = chunk.slice(6).trim()
          if (!jsonStr) continue
          
          try {
            const data = JSON.parse(jsonStr)
            
            // Find the current assistant message
            const currentMsgIndex = chatStore.messages.findIndex(m => m.id === assistantMsgId)
            
            if (currentMsgIndex !== -1) {
                if (data.type === 'text') {
                    // It's a text chunk, append it
                    chatStore.appendTextToMessage(assistantMsgId, data.content)
                } else if (data.type === 'conversation_id') {
                    // Dify 返回的会话 ID，存起来用于下一轮
                    chatStore.setConversationId(data.conversation_id)
                } else if (data.type === 'action_card') {
                    // 工具返回的确认卡片
                    chatStore.appendMessage({
                        id: Date.now().toString() + '_card',
                        role: 'assistant',
                        type: 'action_card',
                        content: data.content || '请确认操作',
                        card: data.card  // 包含 card_type, fields, actions, params
                    })
                }
                scrollToBottom()
            }
          } catch (e) {
            console.error('JSON parse error from SSE:', e, jsonStr)
          }
        }
      }
    }
  } catch (err) {
    console.error('Chat error:', err)
    chatStore.appendMessage({
        id: Date.now().toString() + '_err',
        role: 'assistant',
        content: '请求发生异常：' + String(err),
        type: 'text'
    })
    scrollToBottom()
  } finally {
    isLoading.value = false
  }
}

// Window resize handler to ensure ball stays on screen
const onResize = () => {
    const ballWidth = 60
    const ballHeight = 60
    if (position.value.x > document.documentElement.clientWidth - ballWidth) {
        position.value.x = Math.max(0, document.documentElement.clientWidth - ballWidth)
    }
    if (position.value.y > document.documentElement.clientHeight - ballHeight) {
        position.value.y = Math.max(0, document.documentElement.clientHeight - ballHeight)
    }
}

onMounted(async () => {
    window.addEventListener('resize', onResize)
    if (!chatStore.isHistoryLoaded && userStore.isLoggedIn) {
        await chatStore.loadHistory()
        scrollToBottom()
    }
})

onUnmounted(() => {
    window.removeEventListener('resize', onResize)
})

const handleCardAction = async (msg: any, actionKey: string) => {
    if (!msg.card) return
    const token = userStore.token
    if (!token) { ElMessage.error('未登录'); return }

    try {
        const resp = await fetch('/api/chat/action-card/callback', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
            body: JSON.stringify({
                card_type: msg.card.card_type,
                action_key: actionKey,
                params: msg.card.params || {},
            }),
        })
        const result = await resp.json()
        if (actionKey === 'cancel') {
            ElMessage.info('操作已取消')
        } else {
            ElMessage.success(result.message || '操作成功')
        }
        // 替换卡片为结果文本
        msg.type = 'text'
        msg.content = actionKey === 'cancel' ? '✖️ 操作已取消' : `✅ ${result.message || '操作已确认执行'}`
    } catch (e) {
        ElMessage.error('回调异常: ' + String(e))
    }
}

</script>

<template>
  <div v-if="userStore.isLoggedIn" class="floating-chat-container">
    <!-- 悬浮球 -->
    <div 
      ref="floatingBall"
      class="floating-ball"
      :style="{ left: position.x + 'px', top: position.y + 'px' }"
      @mousedown.prevent="onMouseDown"
      @click="toggleChat"
    >
      <el-icon size="28" color="#fff"><ChatDotRound /></el-icon>
    </div>

    <!-- 侧边栏/抽屉 聊天窗口 -->
    <el-drawer
      v-model="isChatOpen"
      title="智能助手"
      size="400px"
      direction="rtl"
      :with-header="true"
      custom-class="chat-drawer"
    >
      <div class="chat-layout">
        <div class="chat-body" ref="chatBodyRef">
            <div v-if="chatStore.messages.length === 0" class="empty-tip">
                您好，我是您的智能业务助手，有什么我可以帮您的？
            </div>
            
            <div v-for="msg in chatStore.messages" :key="msg.id" :class="['chat-bubble-wrapper', msg.role]">
                <div class="avatar" v-if="msg.role === 'assistant'">🤖</div>
                <div class="bubble-content">
                    
                    <!-- 文本渲染 -->
                    <div v-if="msg.type === 'text' || !msg.type" class="text-message">
                        <!-- 可以按需引入 marked 渲染 Markdown，这里简单用 pre-wrap 保证换行 -->
                        <span style="white-space: pre-wrap;">{{ msg.content }}</span>
                        <!-- 可以在这里加入一个打字光标的样式，如果正处于加载状态且是最后一条消息 -->
                        <span v-if="isLoading && msg.role === 'assistant' && msg === chatStore.messages[chatStore.messages.length - 1]" class="typing-cursor"></span>
                    </div>

                    <!-- Action Card 渲染 -->
                    <div v-else-if="msg.type === 'action_card' && msg.card" class="action-card">
                        <div class="card-header">
                            <el-icon><Select /></el-icon>
                            <span>{{ msg.card.title || '操作确认' }}</span>
                        </div>
                        <div class="card-body">
                            <div v-for="field in (msg.card.fields || [])" :key="field.label" class="card-field">
                                <span class="field-label">{{ field.label }}：</span>
                                <span class="field-value">{{ field.value }}</span>
                            </div>
                        </div>
                        <div class="card-footer">
                            <el-button v-for="action in (msg.card.actions || [])" :key="action.key"
                                size="small"
                                :type="action.style === 'primary' ? 'primary' : action.key === 'cancel' ? 'default' : 'primary'"
                                :plain="action.key === 'cancel'"
                                @click="handleCardAction(msg, action.key)"
                            >{{ action.label }}</el-button>
                        </div>
                    </div>
                </div>
                <div class="avatar" v-if="msg.role === 'user'">👤</div>
            </div>
        </div>
        
        <div class="chat-footer">
            <el-input
                v-model="inputMessage"
                placeholder="输入消息，开启智能分析..."
                @keyup.enter="sendMessage"
                :disabled="isLoading"
            >
                <template #append>
                    <el-button type="primary" @click="sendMessage" :disabled="!inputMessage.trim() || isLoading">
                        发送
                    </el-button>
                </template>
            </el-input>
        </div>
      </div>
    </el-drawer>
  </div>
</template>

<style scoped>
.floating-chat-container {
  z-index: 9999;
}

.floating-ball {
  position: fixed;
  width: 60px;
  height: 60px;
  background: linear-gradient(135deg, #1890ff 0%, #0050b3 100%);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 12px rgba(24, 144, 255, 0.4);
  cursor: pointer;
  z-index: 9999;
  user-select: none;
  transition: box-shadow 0.3s ease, transform 0.2s;
}

.floating-ball:hover {
  box-shadow: 0 6px 16px rgba(24, 144, 255, 0.6);
  transform: scale(1.05);
}

.chat-drawer {
    display: flex;
    flex-direction: column;
}

.chat-layout {
    display: flex;
    flex-direction: column;
    height: 100%;
    /* 配合 element-plus 的 drawer_body 可以适当配置高度以占满整个抽屉 */
}

/* 如果要适配 v-model / :with-header 这些产生的内层 padding，需要使用 :deep() */
:deep(.el-drawer__body) {
    padding: 0;
    display: flex;
    flex-direction: column;
}

.chat-body {
    flex: 1;
    overflow-y: auto;
    padding: 20px;
    background-color: #f7f9fc;
}

.chat-footer {
    padding: 15px 20px;
    background-color: #fff;
    border-top: 1px solid #eba4a4; /* fallback */
    border-top-color: var(--el-border-color-light);
}

.empty-tip {
    text-align: center;
    color: #909399;
    font-size: 13px;
    margin-top: 50px;
}

.chat-bubble-wrapper {
    display: flex;
    margin-bottom: 20px;
    align-items: flex-start;
}

.chat-bubble-wrapper.user {
    justify-content: flex-end;
}

.avatar {
    font-size: 24px;
    margin: 0 10px;
}

.bubble-content {
    max-width: 80%;
    font-size: 14px;
    line-height: 1.5;
}

/* user texts */
.user .text-message {
    background-color: var(--el-color-primary);
    color: #fff;
    padding: 10px 14px;
    border-radius: 12px 0 12px 12px;
    word-break: break-all;
}

/* assistant texts */
.assistant .text-message {
    background-color: #fff;
    color: var(--el-text-color-primary);
    padding: 10px 14px;
    border-radius: 0 12px 12px 12px;
    word-break: break-all;
    box-shadow: 0 1px 4px rgba(0,0,0,0.05);
}

/* action card (mock) */
.action-card {
    background-color: #fff;
    border: 1px solid var(--el-border-color-lighter);
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(0,0,0,0.05);
    width: 260px; /* 略微定宽 */
}

.card-header {
    background-color: #f0f9eb;
    color: #67c23a;
    padding: 10px;
    font-size: 13px;
    font-weight: bold;
    display: flex;
    align-items: center;
    gap: 6px;
    border-bottom: 1px solid var(--el-border-color-lighter);
}

.card-body {
    padding: 12px;
    font-size: 13px;
    color: var(--el-text-color-regular);
}

.card-footer {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 10px;
    border-top: 1px solid var(--el-border-color-lighter);
    background-color: #fafafa;
}

.card-field {
    display: flex;
    padding: 4px 0;
    font-size: 13px;
    line-height: 1.6;
}

.field-label {
    color: #909399;
    min-width: 70px;
    flex-shrink: 0;
}

.field-value {
    color: #303133;
    font-weight: 500;
}

.typing-cursor {
  display: inline-block;
  width: 6px;
  height: 14px;
  background-color: #333;
  margin-left: 2px;
  animation: blink 1s step-end infinite;
}

@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

</style>
