<template>
  <div class="ai-chat-float" title="AI客服" @click="dialogVisible = true">
    <div class="float-core">
      <Icon icon="ep:chat-dot-round" :size="24" color="#fff" />
    </div>
  </div>

  <Dialog
    v-model="dialogVisible"
    width="680px"
    :fullscreen="false"
    class="ai-chat-dialog"
    title="AI客服"
  >
    <template #title>
      <div class="dialog-title">
        <div class="title-icon">
          <Icon icon="ep:service" :size="16" color="#fff" />
        </div>
        <div>
          <div class="title-main">AI客服</div>
          <div class="title-sub">ERP业务助手</div>
        </div>
      </div>
    </template>

    <div class="chat-panel">
      <div ref="messagePanelRef" class="message-panel">
        <template v-if="messages.length === 0">
          <div class="empty-box">
            <div class="empty-icon">
              <Icon icon="ep:chat-line-round" :size="22" />
            </div>
            <div class="empty-title">你好，我是你的ERP智能客服</div>
            <div class="empty-desc">可直接询问采购、销售、库存、流程处理建议</div>
            <div class="quick-list">
              <button
                v-for="item in quickQuestions"
                :key="item"
                class="quick-item"
                type="button"
                @click="handleQuickAsk(item)"
              >
                {{ item }}
              </button>
            </div>
          </div>
        </template>
        <template v-else>
          <div v-for="(m, idx) in messages" :key="idx" class="message-row" :class="`is-${m.role}`">
            <div class="avatar">{{ m.role === 'assistant' ? 'AI' : '我' }}</div>
            <div class="bubble">
              <template v-if="m.role === 'assistant' && m.loading && !m.content">
                <span class="typing-dots" aria-label="AI 正在思考">
                  <i></i>
                  <i></i>
                  <i></i>
                </span>
              </template>
              <template v-else-if="m.role === 'assistant'">
                <div class="md-content" v-dompurify-html="renderMarkdown(m.content)"></div>
              </template>
              <template v-else>
                {{ m.content }}
              </template>
            </div>
          </div>
        </template>
      </div>

      <div class="editor-panel">
        <div class="editor-head">
          <span class="head-tip">支持连续对话</span>
          <el-button link type="primary" @click="handleResetConversation">新对话</el-button>
        </div>
        <div class="editor-main">
          <el-input
            v-model="input"
            type="textarea"
            :rows="3"
            resize="none"
            placeholder="请输入你的问题，按 Enter 发送，Shift+Enter 换行"
            @keydown.enter.exact.prevent="handleSend"
          />
          <el-button type="primary" :disabled="!canSend" :loading="loading" @click="handleSend">
            发送
          </el-button>
        </div>
      </div>
    </div>
  </Dialog>
</template>

<script lang="ts" setup>
import { computed, nextTick, onUnmounted, reactive, ref, watch } from 'vue'
import { Icon } from '@/components/Icon'
import { Dialog } from '@/components/Dialog'
import { ErpAiApi } from '@/api/erp/ai'
import MarkdownIt from 'markdown-it'

const dialogVisible = ref(false)
const input = ref('')
const loading = ref(false)
const conversationId = ref<string>()
const messagePanelRef = ref<HTMLElement>()
const streamController = ref<AbortController>()

const quickQuestions = [
  '帮我总结当前页面业务重点',
  '采购比价单常见风险有哪些？',
  '库存预警怎么快速排查？',
  '销售异常波动如何分析？'
]

interface MessageItem {
  role: 'user' | 'assistant'
  content: string
  loading?: boolean
}

const messages = ref<MessageItem[]>([])
const md = new MarkdownIt({
  html: false,
  linkify: true,
  breaks: true
})

const canSend = computed(() => !loading.value && !!input.value.trim())

const renderMarkdown = (content: string) => {
  return md.render(content || '')
}

const scrollToBottom = async () => {
  await nextTick()
  const panel = messagePanelRef.value
  if (!panel) {
    return
  }
  panel.scrollTop = panel.scrollHeight
}

watch(
  () => messages.value.length,
  async () => {
    await scrollToBottom()
  }
)

watch(
  () => dialogVisible.value,
  async (value) => {
    if (value) {
      await scrollToBottom()
    }
  }
)

const handleQuickAsk = (question: string) => {
  input.value = question
}

const handleResetConversation = () => {
  if (streamController.value) {
    streamController.value.abort()
    streamController.value = undefined
  }
  loading.value = false
  conversationId.value = undefined
  messages.value = []
}

const handleSend = async () => {
  const content = input.value?.trim()
  if (!content || loading.value) return
  const userMessage = reactive<MessageItem>({ role: 'user', content })
  const assistantMessage = reactive<MessageItem>({ role: 'assistant', content: '', loading: true })
  messages.value.push(userMessage)
  messages.value.push(assistantMessage)
  input.value = ''
  loading.value = true
  const ctrl = new AbortController()
  streamController.value = ctrl
  try {
    await ErpAiApi.chatStream({
      data: {
        message: content,
        conversationId: conversationId.value,
        useContext: true
      },
      ctrl,
      onMessage: async (data) => {
        conversationId.value = data?.conversationId || conversationId.value
        if (!data?.answer) {
          return
        }
        assistantMessage.loading = false
        assistantMessage.content += data.answer
        await scrollToBottom()
      }
    })

    if (!assistantMessage.content.trim()) {
      assistantMessage.content = '（无回复）'
    }
  } catch (e: any) {
    if (!assistantMessage.content.trim()) {
      const errMsg = e?.message || 'AI服务暂时不可用，请稍后再试。'
      assistantMessage.content = errMsg
    }
  } finally {
    assistantMessage.loading = false
    loading.value = false
    streamController.value = undefined
  }
}

onUnmounted(() => {
  if (streamController.value) {
    streamController.value.abort()
    streamController.value = undefined
  }
})
</script>

<style lang="scss" scoped>
.ai-chat-float {
  position: fixed;
  right: 24px;
  bottom: 96px;
  z-index: 1200;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: linear-gradient(135deg, #2f6dff 0%, #1c4ed8 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 14px 28px rgb(28 78 216 / 34%);
  transition:
    transform 0.2s ease,
    box-shadow 0.2s ease;

  &::before {
    content: '';
    position: absolute;
    inset: -7px;
    border-radius: 50%;
    border: 1px solid rgb(47 109 255 / 28%);
    animation: floatPulse 2.4s ease-in-out infinite;
  }

  &:hover {
    transform: translateY(-3px);
    box-shadow: 0 18px 34px rgb(28 78 216 / 42%);
  }
}

.float-core {
  display: flex;
  align-items: center;
  justify-content: center;
}

.dialog-title {
  display: flex;
  align-items: center;
  gap: 10px;
}

.title-icon {
  width: 24px;
  height: 24px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #2f6dff 0%, #1c4ed8 100%);
}

.title-main {
  font-size: 16px;
  line-height: 1;
  font-weight: 700;
  color: #1b2a4b;
}

.title-sub {
  margin-top: 4px;
  font-size: 12px;
  line-height: 1;
  color: #6d7f9f;
}

.chat-panel {
  height: min(66vh, 620px);
  min-height: 460px;
  display: flex;
  flex-direction: column;
}

.message-panel {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  border-radius: 12px;
  border: 1px solid #d6e2f5;
  background:
    radial-gradient(circle at 15% 20%, rgb(47 109 255 / 8%) 0, transparent 42%),
    radial-gradient(circle at 85% 80%, rgb(16 185 129 / 10%) 0, transparent 45%),
    #f6f9ff;
}

.empty-box {
  height: 100%;
  min-height: 300px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.empty-icon {
  width: 48px;
  height: 48px;
  border-radius: 14px;
  color: #2f6dff;
  background: rgb(47 109 255 / 12%);
  display: flex;
  align-items: center;
  justify-content: center;
}

.empty-title {
  margin-top: 12px;
  font-size: 16px;
  font-weight: 700;
  color: #1b2a4b;
}

.empty-desc {
  margin-top: 6px;
  font-size: 13px;
  color: #678;
}

.quick-list {
  margin-top: 16px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}

.quick-item {
  border: 1px solid #c7d7f5;
  background: #fff;
  color: #2d4a7a;
  border-radius: 16px;
  padding: 6px 12px;
  font-size: 12px;
  cursor: pointer;
  transition: all 0.2s ease;

  &:hover {
    border-color: #2f6dff;
    color: #2f6dff;
    background: #f6f9ff;
  }
}

.message-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 14px;

  &.is-user {
    flex-direction: row-reverse;

    .avatar {
      background: #1d4ed8;
    }

    .bubble {
      background: linear-gradient(135deg, #2f6dff 0%, #1d4ed8 100%);
      color: #fff;
      border-bottom-right-radius: 6px;
    }
  }

  &.is-assistant {
    .avatar {
      background: #10b981;
    }

    .bubble {
      background: #fff;
      color: #25324a;
      border: 1px solid #d9e4f8;
      border-bottom-left-radius: 6px;
    }
  }
}

.avatar {
  flex: 0 0 30px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  color: #fff;
}

.bubble {
  max-width: calc(100% - 70px);
  padding: 9px 12px;
  border-radius: 12px;
  line-height: 1.55;
  font-size: 13px;
  white-space: pre-wrap;
  word-break: break-word;
}

.md-content {
  white-space: normal;

  :deep(p) {
    margin: 0 0 8px;
  }

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

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

  :deep(li) {
    margin: 4px 0;
  }

  :deep(strong) {
    font-weight: 700;
  }

  :deep(code) {
    padding: 1px 4px;
    border-radius: 4px;
    font-size: 12px;
    background: rgb(15 23 42 / 8%);
  }

  :deep(pre) {
    margin: 8px 0;
    padding: 10px;
    border-radius: 8px;
    overflow-x: auto;
    background: #0f172a;
  }

  :deep(pre code) {
    padding: 0;
    color: #e2e8f0;
    background: transparent;
  }

  :deep(blockquote) {
    margin: 8px 0;
    padding-left: 10px;
    border-left: 3px solid #bfdbfe;
    color: #4b5563;
  }

  :deep(a) {
    color: #2563eb;
    text-decoration: underline;
  }
}

.typing-dots {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 20px;

  i {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #8aa3d4;
    animation: dotBlink 1.1s infinite ease-in-out;
  }

  i:nth-child(2) {
    animation-delay: 0.15s;
  }

  i:nth-child(3) {
    animation-delay: 0.3s;
  }
}

.editor-panel {
  margin-top: 12px;
  border-radius: 12px;
  border: 1px solid #d6e2f5;
  padding: 10px 12px 12px;
  background: #fff;
}

.editor-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}

.head-tip {
  font-size: 12px;
  color: #7a8ba8;
}

.editor-main {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px;
  align-items: end;
}

@media (max-width: 768px) {
  .ai-chat-float {
    right: 16px;
    bottom: 80px;
    width: 52px;
    height: 52px;
  }

  .chat-panel {
    height: min(72vh, 620px);
    min-height: 420px;
  }

  .editor-main {
    grid-template-columns: 1fr;
  }
}

@keyframes floatPulse {
  0%,
  100% {
    transform: scale(1);
    opacity: 0.5;
  }
  50% {
    transform: scale(1.08);
    opacity: 0.15;
  }
}

@keyframes dotBlink {
  0%,
  80%,
  100% {
    transform: scale(0.6);
    opacity: 0.4;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}
</style>
