<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Service, Close, Promotion } from '@element-plus/icons-vue'
import AppHeader from '@/components/layout/AppHeader.vue'
import AppFooter from '@/components/layout/AppFooter.vue'
import { useUserStore } from '@/stores/modules/user'
import { useChatSocket } from '@/composables/useChatSocket'
import {
  createOrRestoreSessionApi,
  getChatMessagesApi,
  sendChatMessageApi,
  closeChatSessionApi,
  chatHeartbeatApi,
  type ChatSessionDetail,
  type ChatMessage
} from '@/api/chat'
import { formatDateTime } from '@/utils/date'

const userStore = useUserStore()
const ws = useChatSocket(userStore.token || '', 'user')

const loading = ref(true)
const sessionDetail = ref<ChatSessionDetail | null>(null)
const messages = ref<ChatMessage[]>([])
const inputText = ref('')
const sending = ref(false)
const messagesRef = ref<HTMLElement>()
let optimisticSeq = 0

function makeClientMsgId() {
  return `usr${Date.now()}_${++optimisticSeq}`
}

function sessionStatus() {
  return sessionDetail.value?.session.status ?? 0
}

function isClosed() {
  return sessionStatus() === 3
}

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

function ackTo(seq: number) {
  if (!sessionDetail.value || !seq) return
  ws.publish({
    type: 'ack',
    sessionId: sessionDetail.value.session.id,
    lastSeq: seq
  })
}

function pushSystemLine(content: string, key = `sys-${Date.now()}`) {
  messages.value.push({
    id: -Date.now() - Math.floor(Math.random() * 1000),
    sessionId: sessionDetail.value?.session.id ?? 0,
    seq: 0,
    senderId: 0,
    senderType: 2,
    type: 'system',
    content,
    isRecalled: 0,
    createTime: new Date().toISOString(),
    clientMsgId: key
  })
  scrollToBottom()
}

async function initSession() {
  loading.value = true
  try {
    sessionDetail.value = await createOrRestoreSessionApi()
    const sid = sessionDetail.value.session.id
    // 订阅会话消息 + 个人事件
    ws.on('message', onChatMessage)
    ws.on('event', onPersonalEvent)
    ws.on('error', onChatError)

    const history = await getChatMessagesApi(sid, { limit: 200 })
    messages.value = history
    if (history.length === 0) {
      pushSystemLine('您好，欢迎来到零食商城客服，请问有什么可以帮您？')
    }
    const maxSeq = history.reduce((mx, m) => Math.max(mx, m.seq || 0), 0)
    if (maxSeq > 0) ackTo(maxSeq)
    scrollToBottom()
  } catch {
    ElMessage.error('客服会话创建失败，请稍后重试')
  } finally {
    loading.value = false
  }
}

function onChatMessage(msg: ChatMessage) {
  if (!msg || !sessionDetail.value || msg.sessionId !== sessionDetail.value.session.id) return
  // 替换本地乐观消息
  if (msg.clientMsgId) {
    const idx = messages.value.findIndex((m) => m.clientMsgId === msg.clientMsgId)
    if (idx >= 0) {
      messages.value[idx] = msg
      scrollToBottom()
      return
    }
  }
  if (messages.value.some((m) => m.id === msg.id)) return
  messages.value.push(msg)
  scrollToBottom()
  if (msg.seq) ackTo(msg.seq)
}

function onPersonalEvent(ev: any) {
  if (!ev) return
  if (ev.eventType === 'session_accepted') {
    if (sessionDetail.value) {
      sessionDetail.value.session.status = 1
      sessionDetail.value.session.statusText = '处理中'
      sessionDetail.value.session.adminName = ev.data?.adminName
    }
    pushSystemLine(`客服${ev.data?.adminName ? '「' + ev.data.adminName + '」' : ''}已接入，正在为您服务`)
  } else if (ev.eventType === 'session_closed') {
    if (sessionDetail.value) {
      sessionDetail.value.session.status = 3
      sessionDetail.value.session.statusText = '已关闭'
    }
    pushSystemLine('本次会话已关闭，感谢您的咨询')
  }
}

function onChatError(ev: any) {
  ElMessage.error(ev?.message || '连接出错，正在重连…')
}

async function sendMessage() {
  const content = inputText.value.trim()
  if (!content || !sessionDetail.value || isClosed() || sending.value) return
  const clientMsgId = makeClientMsgId()
  inputText.value = ''
  sending.value = true
  // 乐观渲染
  messages.value.push({
    id: -Date.now(),
    sessionId: sessionDetail.value.session.id,
    seq: 0,
    senderId: 0,
    senderType: 0,
    type: 'text',
    content,
    isRecalled: 0,
    createTime: new Date().toISOString(),
    clientMsgId
  })
  scrollToBottom()

  const ok = ws.publish({
    type: 'send',
    sessionId: sessionDetail.value.session.id,
    content,
    clientMsgId
  })
  if (!ok) {
    // WebSocket 未连接 → HTTP 兜底
    try {
      const saved = await sendChatMessageApi(sessionDetail.value.session.id, {
        type: 'text',
        content,
        clientMsgId
      })
      const idx = messages.value.findIndex((m) => m.clientMsgId === clientMsgId)
      if (idx >= 0) messages.value[idx] = saved
      else messages.value.push(saved)
      scrollToBottom()
      if (saved.seq) ackTo(saved.seq)
    } catch {
      ElMessage.error('发送失败，请检查网络后重试')
    }
  }
  sending.value = false
}

async function handleClose() {
  if (!sessionDetail.value) return
  try {
    await ElMessageBox.confirm('确定结束本次咨询吗？结束后将无法继续发送消息。', '结束会话', {
      type: 'warning',
      confirmButtonText: '结束会话',
      cancelButtonText: '继续咨询'
    })
    const ok = ws.publish({ type: 'close', sessionId: sessionDetail.value.session.id })
    if (!ok) {
      await closeChatSessionApi(sessionDetail.value.session.id)
    }
    if (sessionDetail.value) {
      sessionDetail.value.session.status = 3
      sessionDetail.value.session.statusText = '已关闭'
    }
    pushSystemLine('本次会话已关闭，感谢您的咨询')
  } catch {
    /* 用户取消 */
  }
}

async function handleRestart() {
  // 关闭/历史会话：重新发起一个新会话
  await initSession()
  ElMessage.success('已创建新的会话')
}

// ==================== 生命周期 ====================
let heartbeatTimer: number | undefined

onMounted(() => {
  ws.connect()
  initSession()
  heartbeatTimer = window.setInterval(() => {
    chatHeartbeatApi().catch(() => {})
  }, 30000)
})

onBeforeUnmount(() => {
  ws.disconnect()
  if (heartbeatTimer) window.clearInterval(heartbeatTimer)
})
</script>

<template>
  <div class="chat-page">
    <AppHeader />

    <main class="chat-main">
      <div class="chat-container">
        <!-- 顶栏 -->
        <div class="chat-top">
          <div class="chat-top-left">
            <div class="service-icon">
              <el-icon :size="22"><Service /></el-icon>
            </div>
            <div>
              <h2>在线客服</h2>
              <p class="chat-sub">
                <span :class="['ws-dot', { online: ws.connected.value }]" />
                {{ ws.connected.value ? '实时连接中' : '连接中，请稍候…' }}
                <template v-if="sessionDetail">
                  · 会话号 {{ sessionDetail.session.sessionNo }}
                </template>
              </p>
            </div>
          </div>
          <div class="chat-top-right">
            <el-tag v-if="sessionDetail" :type="isClosed() ? 'info' : sessionStatus() === 1 ? 'success' : 'warning'">
              {{ sessionDetail.session.statusText || (isClosed() ? '已关闭' : '等待接入') }}
            </el-tag>
            <el-button
              v-if="isClosed()"
              type="primary"
              round
              size="small"
              @click="handleRestart"
            >重新咨询</el-button>
            <el-button
              v-else
              round
              size="small"
              :icon="Close"
              @click="handleClose"
            >结束会话</el-button>
          </div>
        </div>

        <!-- 消息区 -->
        <div ref="messagesRef" v-loading="loading" class="chat-messages">
          <div
            v-for="m in messages"
            :key="m.clientMsgId || m.id"
            :class="['msg-row', m.senderType === 0 ? 'mine' : m.senderType === 2 ? 'system' : 'theirs']"
          >
            <template v-if="m.senderType === 2">
              <div class="system-msg">{{ m.content }}</div>
            </template>
            <template v-else>
              <div class="msg-avatar">
                <el-icon v-if="m.senderType === 1"><Service /></el-icon>
                <span v-else>{{ (userStore.nickname || '我').slice(0, 1) }}</span>
              </div>
              <div class="msg-bubble">
                <span class="msg-text">{{ m.content }}</span>
                <span class="msg-time">{{ formatDateTime(m.createTime) }}</span>
              </div>
            </template>
          </div>
        </div>

        <!-- 输入区 -->
        <div class="chat-input">
          <el-input
            v-model="inputText"
            type="textarea"
            :rows="3"
            :disabled="isClosed()"
            :placeholder="isClosed() ? '会话已结束，点击右上角重新咨询' : '请输入您的问题，Enter 换行 / Ctrl+Enter 发送'"
            resize="none"
            @keydown.ctrl.enter.prevent="sendMessage"
          />
          <div class="chat-actions">
            <span v-if="sessionStatus() === 0" class="waiting-tip">
              <el-icon class="is-loading"><Promotion /></el-icon>
              正在为您分配客服…
            </span>
            <el-button
              type="primary"
              round
              :loading="sending"
              :disabled="!inputText.trim() || isClosed()"
              @click="sendMessage"
            >发送 (Ctrl+Enter)</el-button>
          </div>
        </div>
      </div>
    </main>

    <AppFooter />
  </div>
</template>

<style lang="scss" scoped>
.chat-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: $bg-page;
}
.chat-main {
  flex: 1;
  width: 100%;
  max-width: $container-max;
  margin: 0 auto;
  padding: $section-gap 16px;
}
.chat-container {
  display: flex;
  flex-direction: column;
  height: calc(100vh - 220px);
  min-height: 480px;
  background: $bg-surface;
  border: 2px solid $color-border;
  border-radius: $radius-lg;
  box-shadow: $clay-shadow-md;
  overflow: hidden;
}
.chat-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 20px;
  border-bottom: 2px solid $color-border;
  background: linear-gradient(135deg, $color-primary-light, $bg-surface);
  flex-wrap: wrap;

  h2 {
    margin: 0;
    font-size: $font-size-xl;
    color: $color-text-primary;
  }
}
.chat-top-left {
  display: flex;
  align-items: center;
  gap: 12px;
}
.service-icon {
  width: 46px;
  height: 46px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: $radius-full;
  background: linear-gradient(135deg, $color-primary, $color-secondary);
  color: #fff;
  box-shadow: $clay-shadow-sm;
}
.chat-sub {
  margin: 4px 0 0;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: $font-size-xs;
  color: $color-text-secondary;

  .ws-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: $color-warning;

    &.online {
      background: $color-success;
    }
  }
}
.chat-top-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.msg-row {
  display: flex;
  gap: 10px;
  align-items: flex-start;

  &.mine {
    flex-direction: row-reverse;
  }
  &.system {
    justify-content: center;
  }
}
.msg-avatar {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: $radius-full;
  background: linear-gradient(135deg, $color-primary, $color-secondary);
  color: #fff;
  font-size: 14px;
}
.msg-bubble {
  max-width: 62%;
  padding: 10px 14px 8px;
  background: #fff;
  border: 2px solid $color-border;
  border-radius: $radius-md;
  box-shadow: $clay-shadow-sm;

  .msg-text {
    display: block;
    white-space: pre-wrap;
    word-break: break-word;
    color: $color-text-primary;
  }
  .msg-time {
    display: block;
    margin-top: 4px;
    font-size: 11px;
    color: $color-text-placeholder;
    text-align: right;
  }
}
.msg-row.mine .msg-bubble {
  background: linear-gradient(135deg, $color-primary, $color-primary-dark);
  border-color: transparent;

  .msg-text {
    color: #fff;
  }
  .msg-time {
    color: rgba(255, 255, 255, 0.75);
  }
}
.system-msg {
  padding: 5px 14px;
  border-radius: $radius-full;
  background: rgba(122, 106, 98, 0.12);
  color: $color-text-secondary;
  font-size: $font-size-xs;
  text-align: center;
}

.chat-input {
  border-top: 2px solid $color-border;
  padding: 14px 20px 16px;
  background: $bg-surface;
}
.chat-actions {
  margin-top: 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;

  .waiting-tip {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: $font-size-xs;
    color: $color-text-secondary;
  }
}

@media (max-width: 768px) {
  .chat-main {
    padding: 16px 12px;
  }
  .chat-container {
    height: calc(100vh - 170px);
  }
  .msg-bubble {
    max-width: 78%;
  }
}
</style>
