<script setup lang="ts">
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { ArrowLeft, Close, Loading, Picture } from '@element-plus/icons-vue'
import {
  fetchChat,
  fetchChatMessages,
  markChatRead,
  sendChatMessage,
  type ChatConversation,
  type ChatMessage,
} from '@/features/chats/api/chats'
import { uploadFile } from '@/shared/api/files'
import ChatAttachmentImage from '@/components/ChatAttachmentImage.vue'
import { useChatSSE, type ChatEvent } from '@/features/chats/composables/useChatSSE'
import { useDesktopNotification } from '@/features/chats/composables/useDesktopNotification'
import { formatDateMinute } from '@/utils/time'

const currentUserId = Number(localStorage.getItem('user_id') || 0)

const route = useRoute()
const router = useRouter()
const conversation = ref<ChatConversation | null>(null)
const messages = ref<ChatMessage[]>([])
const loading = ref(false)
const sending = ref(false)
const uploading = ref(false)
const content = ref('')
const attachments = ref<string[]>([])
const listRef = ref<HTMLElement | null>(null)
const fileInputRef = ref<HTMLInputElement | null>(null)

const conversationID = computed(() => Number(route.params.id || 0))
const canSend = computed(() => content.value.trim() !== '' || attachments.value.length > 0)
const memberText = computed(() => {
  const participants = conversation.value?.participants || []
  if (participants.length === 0) return conversation.value?.type === 'general_support' ? '平台客服' : '订单群聊'
  return participants.map(item => `${roleLabel(item.role)}：${item.display_name}`).join(' / ')
})

// 桌面通知
const desktopNotification = useDesktopNotification('user')

function handleSSEEvent(event: ChatEvent) {
  if (event.type === 'new_message' && event.conversation_id === conversationID.value) {
    const msg = event.message
    if (msg) {
      const isSelf = msg.sender_type === 'user' && msg.sender_id === currentUserId

      appendMessage({
        id: msg.id,
        conversation_id: msg.conversation_id,
        sender_type: msg.sender_type as ChatMessage['sender_type'],
        sender_id: msg.sender_id,
        sender_role: msg.sender_role as ChatMessage['sender_role'],
        sender_name: msg.sender_name,
        sender_avatar: '',
        is_self: isSelf,
        content_type: msg.content_type as ChatMessage['content_type'],
        content: msg.content,
        attachment_urls: msg.attachment_urls || [],
        created_at: msg.created_at,
      })

      markChatRead(conversationID.value).catch(() => {})

      // 不是自己发送的消息才发送通知
      if (!isSelf) {
        desktopNotification.notify(event, conversationID.value)
      }
    }
  }
  if (event.type === 'conversation_updated' && event.conversation_id === conversationID.value) {
    loadConversation()
  }
}

const { onEvent } = useChatSSE('user', '/api/chats/events')
onEvent(handleSSEEvent)

onMounted(async () => {
  // 静默请求通知权限（用户交互后才会弹窗）
  desktopNotification.requestPermissionSilently()
  await loadAll()
})

watch(conversationID, async (id, oldId) => {
  if (id && id !== oldId) {
    await loadAll()
  }
})

async function loadAll() {
  if (!conversationID.value) return
  loading.value = true
  conversation.value = null
  messages.value = []
  try {
    const [chat] = await Promise.all([
      fetchChat(conversationID.value),
      loadMessages(true),
    ])
    conversation.value = chat
    await markChatRead(conversationID.value)
  } catch {
    ElMessage.error('加载会话失败')
  } finally {
    loading.value = false
  }
}

async function loadConversation() {
  if (!conversationID.value) return
  try {
    conversation.value = await fetchChat(conversationID.value)
  } catch { /* ignore */ }
}

async function loadMessages(scrollToBottom = true) {
  if (!conversationID.value) return
  const res = await fetchChatMessages(conversationID.value, 1, 100)
  messages.value = res.items
  if (scrollToBottom) {
    await nextTick()
    scrollBottom()
  }
}

async function handleSend() {
  const text = content.value.trim()
  const imageUrls = [...attachments.value]
  if ((!text && imageUrls.length === 0) || sending.value || uploading.value) return
  sending.value = true
  try {
    const sent = await sendChatMessage(conversationID.value, text, imageUrls)
    appendMessage(sent)
    content.value = ''
    attachments.value = []
    await loadConversation()
  } catch {
    ElMessage.error('发送失败')
  } finally {
    sending.value = false
  }
}

function pickImages() {
  if (uploading.value || attachments.value.length >= 9) return
  fileInputRef.value?.click()
}

async function handleImageChange(event: Event) {
  const input = event.target as HTMLInputElement
  const files = Array.from(input.files || [])
  input.value = ''
  if (files.length === 0) return
  await uploadImages(files)
}

async function handlePaste(event: ClipboardEvent) {
  const items = Array.from(event.clipboardData?.items || [])
  const imageFiles = items
    .filter(item => item.type.startsWith('image/'))
    .map(item => item.getAsFile())
    .filter(Boolean) as File[]

  if (imageFiles.length > 0) {
    event.preventDefault()
    await uploadImages(imageFiles)
  }
}

async function uploadImages(files: File[]) {
  const slots = 9 - attachments.value.length
  if (slots <= 0) {
    ElMessage.warning('每条消息最多发送 9 张图片')
    return
  }
  uploading.value = true
  try {
    for (const file of files.slice(0, slots)) {
      if (!['image/jpeg', 'image/png', 'image/webp'].includes(file.type) || file.size > 25 * 1024 * 1024) {
        ElMessage.warning(`${file.name} 不符合图片规则`)
        continue
      }
      const uploaded = await uploadFile(file, 'chat')
      attachments.value.push(uploaded.url)
    }
    if (files.length > slots) {
      ElMessage.warning('每条消息最多发送 9 张图片')
    }
  } catch {
    ElMessage.error('图片上传失败')
  } finally {
    uploading.value = false
  }
}

function removeAttachment(index: number) {
  attachments.value.splice(index, 1)
}

function appendMessage(message: ChatMessage) {
  if (messages.value.some(item => item.id === message.id)) return
  messages.value = [...messages.value, message]
  nextTick(() => scrollBottom())
}

function scrollBottom() {
  const el = listRef.value
  if (!el) return
  el.scrollTop = el.scrollHeight
}

function roleLabel(role: string) {
  const map: Record<string, string> = {
    renter: '租客',
    owner: '号主',
    support: '客服',
    customer: '咨询',
    system: '系统',
    admin: '管理员',
  }
  return map[role] || '成员'
}

function senderLabel(message: ChatMessage) {
  if (message.sender_type === 'system') return '系统'
  // 管理员消息特殊标识
  if (message.sender_role === 'admin') {
    return `管理员 · ${message.sender_name}`
  }
  return `${roleLabel(message.sender_role)} · ${message.sender_name}`
}

function handleKeydown(e: KeyboardEvent) {
  if (e.key === 'Enter' && !e.shiftKey) {
    e.preventDefault()
    handleSend()
  }
}
</script>

<template>
  <section class="page chat-page">
    <div class="chat-workbench">
      <!-- Header -->
      <div class="chat-header">
        <button class="back-btn" type="button" @click="router.push('/messages')">
          <el-icon :size="18"><ArrowLeft /></el-icon>
          <span>返回</span>
        </button>
        <div class="chat-title">
          <h2>{{ conversation?.title || '客服会话' }}</h2>
          <p>{{ memberText }}</p>
        </div>
        <el-button
          v-if="conversation?.order_id"
          type="primary"
          link
          @click="router.push(`/orders/${conversation.order_id}`)"
        >
          查看订单
        </el-button>
      </div>

      <!-- Messages -->
      <div ref="listRef" v-loading="loading" class="message-list">
        <div v-if="loading && messages.length === 0" class="loading-placeholder">
          <el-icon class="is-loading" :size="24"><Loading /></el-icon>
        </div>
        <el-empty v-else-if="!loading && messages.length === 0" description="暂无消息" />

        <div
          v-for="item in messages"
          :key="item.id"
          class="message-row"
          :class="{
            self: item.is_self,
            system: item.sender_type === 'system',
            admin: item.sender_role === 'admin' && !item.is_self
          }"
        >
          <template v-if="item.sender_type === 'system'">
            <span class="system-message">{{ item.content }}</span>
          </template>
          <template v-else>
            <div class="avatar" :class="{ self: item.is_self }">{{ roleLabel(item.sender_role).slice(0, 1) }}</div>
            <div class="bubble-wrap" :class="{ self: item.is_self }">
              <span class="sender-name" :class="{ 'admin-label': item.sender_role === 'admin' && !item.is_self }">
                {{ senderLabel(item) }}
              </span>
              <div v-if="item.content" class="bubble">{{ item.content }}</div>
              <div v-if="item.attachment_urls.length > 0" class="message-attachments">
                <ChatAttachmentImage
                  v-for="url in item.attachment_urls"
                  :key="url"
                  :source="url"
                />
              </div>
              <span class="message-time">{{ formatDateMinute(item.created_at) }}</span>
            </div>
          </template>
        </div>
      </div>

      <!-- Composer -->
      <div class="composer">
        <div v-if="attachments.length > 0" class="pending-attachments">
          <div v-for="(url, index) in attachments" :key="url" class="pending-item">
            <ChatAttachmentImage :source="url" />
            <button type="button" class="remove-attachment" @click="removeAttachment(index)">
              <el-icon :size="14"><Close /></el-icon>
            </button>
          </div>
        </div>
        <input
          ref="fileInputRef"
          class="hidden-file"
          type="file"
          accept="image/jpeg,image/png,image/webp"
          multiple
          @change="handleImageChange"
        >
        <el-input
          v-model="content"
          type="textarea"
          :rows="3"
          :maxlength="1000"
          show-word-limit
          placeholder="发送消息..."
          resize="none"
          @keydown="handleKeydown"
          @paste="handlePaste"
        />
        <div class="composer-actions">
          <span class="composer-hint">Enter 发送，Shift+Enter 换行</span>
          <div class="composer-buttons">
            <el-button :icon="Picture" :loading="uploading" :disabled="attachments.length >= 9" @click="pickImages">
              图片
            </el-button>
            <el-button type="primary" :disabled="!canSend || sending || uploading" :loading="sending" @click="handleSend">
              发送
            </el-button>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>


<style scoped>
.chat-page {
  max-width: 1720px;
  margin: 0 auto;
}

.chat-workbench {
  display: flex;
  flex-direction: column;
  height: calc(100vh - 56px - 40px);
  border: 1px solid #e8edf3;
  border-radius: 12px;
  background: #fff;
  overflow: hidden;
}

.chat-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid #eef1f5;
  background: #fafbfc;
  flex-shrink: 0;
}

.back-btn {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 10px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: #5a6577;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}

.back-btn:hover {
  background: #f0f5ff;
  color: #1477ff;
}

.chat-title {
  flex: 1;
  min-width: 0;
}

.chat-title h2 {
  margin: 0;
  overflow: hidden;
  color: #17233d;
  font-size: 15px;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-title p {
  margin: 2px 0 0;
  color: #6b7785;
  font-size: 12px;
}

.message-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 16px;
}

.loading-placeholder {
  display: flex;
  justify-content: center;
  padding: 60px 0;
  color: #a0aab6;
}

.message-row {
  display: flex;
  gap: 10px;
  margin-bottom: 16px;
}

.message-row.self {
  flex-direction: row-reverse;
}

.message-row.system {
  justify-content: center;
}

.avatar {
  display: grid;
  flex: none;
  width: 36px;
  height: 36px;
  place-items: center;
  border-radius: 50%;
  background: #1477ff;
  color: #fff;
  font-size: 14px;
  font-weight: 800;
}

.avatar.self {
  background: #10b981;
}

.bubble-wrap {
  display: flex;
  max-width: 60%;
  flex-direction: column;
  align-items: flex-start;
}

.bubble-wrap.self {
  align-items: flex-end;
}

.sender-name {
  margin-bottom: 4px;
  color: #8a94a6;
  font-size: 12px;
}

.bubble {
  max-width: 100%;
  padding: 10px 14px;
  border-radius: 10px;
  background: #f4f6f8;
  color: #17233d;
  font-size: 14px;
  line-height: 1.5;
  word-break: break-word;
}

.message-row.self .bubble {
  background: #dff5eb;
  color: #0f5132;
}

.message-row.admin .bubble {
  background: #fff4e6;
  border-left: 3px solid #ff9800;
}

.message-row.admin .avatar {
  background: #ff9800;
}

.admin-label {
  color: #ff9800;
  font-weight: 600;
}

.message-attachments {
  display: grid;
  gap: 6px;
  margin-top: 6px;
}

.message-time {
  margin-top: 4px;
  color: #a1a8b4;
  font-size: 11px;
}

.system-message {
  max-width: 80%;
  padding: 6px 12px;
  border-radius: 8px;
  background: #e6ebf2;
  color: #6b7280;
  font-size: 12px;
  line-height: 1.4;
  text-align: center;
}

.composer {
  flex-shrink: 0;
  padding: 12px 16px;
  border-top: 1px solid #eef1f5;
  background: #fafbfc;
}

.pending-attachments {
  display: flex;
  gap: 8px;
  margin-bottom: 10px;
  overflow-x: auto;
}

.pending-item {
  position: relative;
  flex: none;
}

.pending-item :deep(.chat-image-button) {
  width: 86px;
  height: 86px;
}

.pending-item :deep(.chat-image-button img) {
  height: 86px;
}

.remove-attachment {
  position: absolute;
  top: 4px;
  right: 4px;
  display: grid;
  width: 22px;
  height: 22px;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(17, 24, 39, 0.72);
  color: #fff;
  cursor: pointer;
}

.hidden-file {
  display: none;
}

.composer-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 8px;
}

.composer-buttons {
  display: flex;
  gap: 8px;
}

.composer-hint {
  color: #a0aab6;
  font-size: 12px;
}
</style>
