<template>
  <div class="wisdom-management">
    <div class="wisdom-backdrop" aria-hidden="true">
      <div class="wisdom-grid"></div>
      <div class="wisdom-orbit wisdom-orbit-a"></div>
      <div class="wisdom-orbit wisdom-orbit-b"></div>
      <div class="wisdom-scan"></div>
    </div>

    <aside v-if="leftPanels.length" class="wisdom-side wisdom-side-left">
      <section v-for="panel in leftPanels" :key="panel.id" class="wisdom-side-panel" :class="`is-${panel.type}`">
        <header v-if="!panel.chart" class="wisdom-side-header">
          <div>
            <span>{{ panel.kicker }}</span>
            <strong>{{ panel.title }}</strong>
          </div>
          <em v-if="panel.badge">{{ panel.badge }}</em>
        </header>
        <div v-if="panel.items?.length" class="wisdom-side-metrics">
          <div v-for="item in panel.items" :key="`${panel.id}-${item.label}`" class="wisdom-side-metric">
            <span>{{ item.label }}</span>
            <strong>{{ item.value }}</strong>
            <em v-if="item.note">{{ item.note }}</em>
          </div>
        </div>
        <div v-if="panel.text" class="wisdom-side-text" v-html="renderMarkdown(panel.text)"></div>
        <div v-if="panel.actions?.length" class="wisdom-side-actions">
          <button
            v-for="action in panel.actions"
            :key="action.question"
            type="button"
            :disabled="loading"
            @click="sendMessage(action.question)"
          >
            {{ action.label }}
          </button>
        </div>
      </section>
    </aside>

    <aside v-if="rightPanels.length" class="wisdom-side wisdom-side-right">
      <section v-for="panel in rightPanels" :key="panel.id" class="wisdom-side-panel" :class="`is-${panel.type}`">
        <header v-if="!panel.chart" class="wisdom-side-header">
          <div>
            <span>{{ panel.kicker }}</span>
            <strong>{{ panel.title }}</strong>
          </div>
          <em v-if="panel.badge">{{ panel.badge }}</em>
        </header>
        <smart-chart-renderer
          v-if="panel.chart"
          class="wisdom-side-chart"
          :chart="panel.chart"
        />
        <div v-if="panel.items?.length" class="wisdom-side-metrics">
          <div v-for="item in panel.items" :key="`${panel.id}-${item.label}`" class="wisdom-side-metric">
            <span>{{ item.label }}</span>
            <strong>{{ item.value }}</strong>
            <em v-if="item.note">{{ item.note }}</em>
          </div>
        </div>
        <div v-if="panel.text" class="wisdom-side-text" v-html="renderMarkdown(panel.text)"></div>
        <div v-if="panel.actions?.length" class="wisdom-side-actions">
          <button
            v-for="action in panel.actions"
            :key="action.question"
            type="button"
            :disabled="loading"
            @click="sendMessage(action.question)"
          >
            {{ action.label }}
          </button>
        </div>
      </section>
    </aside>

    <main class="wisdom-chat">
      <div ref="dialogueRef" class="wisdom-dialogue" :class="{ 'is-empty': !messages.length }">
	        <section v-if="!messages.length" class="wisdom-empty-state">
	          <div class="wisdom-empty-mark">
	            <span></span>
	            <strong>AI</strong>
	          </div>
	          <h2>智慧管理问答</h2>
	          <p>{{ welcomeText }}</p>
	          <div class="wisdom-prompt-grid">
	            <button
	              v-for="prompt in quickPrompts"
	              :key="prompt.question"
	              type="button"
	              :disabled="loading"
	              @click="sendMessage(prompt.question)"
	            >
	              {{ prompt.label }}
	            </button>
	          </div>
	        </section>

        <article
          v-for="message in messages"
          :key="message.id"
          class="wisdom-message"
          :class="`is-${message.role}`"
        >
          <div v-if="message.role === 'assistant'" class="wisdom-message-mark">
            <span>牧</span>
          </div>
          <div class="wisdom-bubble">
            <template v-if="message.role === 'user'">
              {{ message.text }}
            </template>
            <template v-else>
              <div v-if="message.steps?.length" class="wisdom-steps">
                <div v-for="step in message.steps" :key="step" class="wisdom-step">
                  <span></span>{{ step }}
                </div>
              </div>
              <div v-if="message.digesting" class="wisdom-digesting">
                <span></span><span></span><span></span>
                <em>{{ message.digestText || "小牧正在消化中" }}</em>
              </div>
              <div v-if="message.visibleText" class="wisdom-answer" v-html="renderMarkdown(message.visibleText)"></div>
              <span v-if="message.streaming" class="wisdom-stream-caret"></span>
	              <div v-if="message.suggestions?.length && (message.allowFollowUp || (!message.digesting && !message.streaming))" class="wisdom-suggestion-list">
	                <button
	                  v-for="suggestion in message.suggestions"
	                  :key="suggestion.question"
	                  type="button"
	                  :disabled="loading"
	                  @click="sendMessage(suggestion.question)"
	                >
	                  {{ suggestion.label }}
	                </button>
	              </div>
	              <div v-if="message.choices?.length" class="wisdom-choice-list">
                <button
                  v-for="(choice, index) in message.choices"
                  :key="`${choice.key || 'choice'}-${choice.value || index}`"
                  type="button"
                  :disabled="loading"
                  @click="selectBalanceChoice(choice)"
                >
                  {{ choice.name || choice.label || choice.value }}
                </button>
              </div>
            </template>
          </div>
        </article>
      </div>

      <form class="wisdom-input-bar" @submit.prevent="sendMessage()">
        <div class="wisdom-input-shell">
          <span class="wisdom-input-icon"></span>
          <input
            v-model="currentText"
            :disabled="loading"
            autocomplete="off"
            placeholder="输入牧户、区域或草畜平衡问题"
            @keydown.enter.prevent="sendMessage()"
          />
          <button
            type="button"
            class="wisdom-voice-button"
            :class="{ 'is-listening': voiceListening }"
            :disabled="loading || !voiceSupported"
            :title="voiceSupported ? (voiceListening ? '停止语音输入' : '语音输入') : '当前浏览器不支持语音输入'"
            :aria-label="voiceSupported ? (voiceListening ? '停止语音输入' : '语音输入') : '当前浏览器不支持语音输入'"
            @click="toggleVoiceInput"
          >
            <span></span>
          </button>
          <button class="wisdom-submit-button" type="submit" :disabled="loading || !currentText.trim()">
            <span v-if="loading" class="wisdom-button-loading"></span>
            <span>{{ loading ? "消化中" : "发送" }}</span>
          </button>
        </div>
      </form>
    </main>
  </div>
</template>

<script setup>
import { defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue"
import {
  askStatQuestion,
  getLivestockBalance,
  getXiaomuWelcome,
  queryBalanceTargets,
} from "@/services/smartApi"
import { getSmartChartsForQuestion } from "@/services/smartChartApi"
import { getRiskLevel } from "@/views/grassLivestockBalance/data"

const SmartChartRenderer = defineAsyncComponent({
  loader: () => import("./components/SmartChartRenderer.vue"),
  loadingComponent: {
    template: '<div class="smart-chart-loading">正在绘制统计图表</div>',
  },
})

const props = defineProps({
  active: {
    type: Boolean,
    default: true,
  },
})
const currentText = ref("")
const messages = ref([])
const loading = ref(false)
const dialogueRef = ref(null)
const leftPanels = ref([])
const rightPanels = ref([])
const voiceSupported = ref(false)
const voiceListening = ref(false)
const voiceError = ref("")
const welcomeText = ref("可以直接询问牦牛数量、草地面积、牧户情况、饲草产量、林地面积、基地分布、畜群结构和交易流通等统计问题。")
const quickPrompts = [
  { label: "牦牛分布", question: "牦牛分布" },
  { label: "畜群结构", question: "牦牛结构占比" },
  { label: "草地面积", question: "草地面积分布" },
  { label: "草畜平衡", question: "红原县草畜平衡建议" },
  { label: "湿地类型", question: "湿地类型结构" },
  { label: "公益林保护", question: "公益林保护等级统计" },
]
let streamTimer = null
let speechRecognition = null

onMounted(() => {
  getXiaomuWelcome()
    .then((data) => {
      const text = data?.value || data?.text || data
      if (typeof text === "string" && text.trim()) welcomeText.value = text
    })
    .catch(() => {})
  initSpeechRecognition()
})

onBeforeUnmount(() => {
  stopStreaming()
  stopVoiceInput()
})

watch(
  () => props.active,
  (active) => {
    if (active) return
    stopStreaming()
    stopVoiceInput()
    loading.value = false
    messages.value = messages.value.map((message) =>
      message.streaming ? { ...message, streaming: false, digesting: false } : message,
    )
  },
)

function createId() {
  return `${Date.now()}-${Math.random().toString(16).slice(2)}`
}

function normalizeYakText(value) {
  return String(value || "")
    .replace(/耗牛/g, "牦牛")
    .replace(/犛牛/g, "牦牛")
}

function normalizeQuestionText(value) {
  return normalizeYakText(value).trim()
}

function initSpeechRecognition() {
  if (typeof window === "undefined") return
  const Recognition = window.SpeechRecognition || window.webkitSpeechRecognition
  voiceSupported.value = Boolean(Recognition)
  if (!Recognition) return

  speechRecognition = new Recognition()
  speechRecognition.lang = "zh-CN"
  speechRecognition.continuous = false
  speechRecognition.interimResults = true
  speechRecognition.maxAlternatives = 1

  speechRecognition.onstart = () => {
    voiceListening.value = true
    voiceError.value = ""
  }
  speechRecognition.onresult = (event) => {
    const results = Array.from(event.results || [])
    const transcript = results
      .map((result) => result?.[0]?.transcript || "")
      .join("")
      .trim()
    if (transcript) currentText.value = transcript
  }
  speechRecognition.onerror = (event) => {
    voiceListening.value = false
    voiceError.value = event?.error || "语音输入失败"
  }
  speechRecognition.onend = () => {
    voiceListening.value = false
  }
}

function toggleVoiceInput() {
  if (!speechRecognition || loading.value) return
  if (voiceListening.value) {
    stopVoiceInput()
    return
  }
  try {
    voiceError.value = ""
    speechRecognition.start()
  } catch (error) {
    voiceError.value = error?.message || "语音输入启动失败"
    voiceListening.value = false
  }
}

function stopVoiceInput() {
  if (!speechRecognition || !voiceListening.value) return
  try {
    speechRecognition.stop()
  } catch (error) {
    voiceError.value = error?.message || "语音输入已结束"
  } finally {
    voiceListening.value = false
  }
}

async function sendMessage(text = currentText.value) {
  const question = normalizeQuestionText(text)
  if (!question || loading.value) return
  stopVoiceInput()

  const userMessage = {
    id: createId(),
    role: "user",
    text: question,
  }
  const assistantMessage = {
    id: createId(),
    role: "assistant",
    steps: [`正在理解：${question}`],
    digesting: true,
    digestText: "小牧正在匹配业务数据",
    visibleText: "",
    fullText: "",
    streaming: false,
    choices: [],
    charts: [],
    insights: [],
    suggestions: [],
  }

  messages.value = [...messages.value, userMessage, assistantMessage]
  currentText.value = ""
  loading.value = true
  await scrollToBottom()

  if (shouldUseBalanceFlow(question)) {
    await handleBalanceQuestion(question, assistantMessage.id)
    return
  }

  await handleStatQuestion(question, assistantMessage.id)
}

async function handleStatQuestion(question, assistantId) {
  patchAssistant(assistantId, {
    digestText: "正在调用AI统计问答",
  })

  const pagePath = window.location.hash || window.location.pathname
  const [answerResult, chartResult] = await Promise.allSettled([
    askStatQuestion(question, pagePath, "智慧管理"),
    getSmartChartsForQuestion(question, pagePath, "智慧管理"),
  ])
  const charts = chartResult.status === "fulfilled" ? chartResult.value || [] : []
  const chartError = chartResult.status === "rejected" ? chartResult.reason : null

  if (answerResult.status === "fulfilled") {
    const data = answerResult.value
    const aiAnswer = normalizeYakText(extractAiAnswer(data))
    if (aiAnswer && !isAiFailureAnswer(aiAnswer)) {
      const displayAnswer = chartError
        ? `${aiAnswer}\n\n**图表状态**\n- ${getFriendlyErrorMessage(chartError, "图表接口暂时不可用，已先返回AI文字分析。")}`
        : aiAnswer
      const insights = charts.length ? buildChartInsights(charts) : []
      const suggestions = buildFollowUpSuggestions(question, charts)
      const steps = [`正在理解：${question}`, "已生成AI回答"]
      if (charts.length) {
        steps.push(`右侧已生成 ${charts.length} 个数据图表`)
      } else if (chartError) {
        steps.push("图表生成暂时不可用")
      }

      patchAssistant(assistantId, {
        digesting: false,
        steps,
        insights,
        suggestions,
        allowFollowUp: true,
      })
      updateDynamicPanels({
        question,
        answer: displayAnswer,
        charts,
        insights,
        suggestions,
        aiPanels: extractAiPanels(data),
      })
      streamAssistantText(assistantId, displayAnswer, charts)
      return
    }

    if (charts.length) {
      const answer = "AI文字分析暂时不可用，已先展示统计图表。请稍后重新提问，或换一个更具体的问题。"
      const insights = buildChartInsights(charts)
      const suggestions = buildFollowUpSuggestions(question, charts)
      patchAssistant(assistantId, {
        digesting: false,
        steps: [`正在理解：${question}`, "AI文字分析暂时不可用", `右侧已生成 ${charts.length} 个数据图表`],
        insights,
        suggestions,
        allowFollowUp: true,
      })
      updateDynamicPanels({ question, answer, charts, insights, suggestions })
      streamAssistantText(assistantId, answer, charts)
      return
    }

    const errorText = getFriendlyErrorMessage(
      new Error(aiAnswer || "AI接口没有返回有效回答"),
      "AI问答暂时没有返回有效结果，请稍后再试，或换一个更具体的问题。",
    )
    updateDynamicPanels({ question, answer: errorText, charts: [], insights: [], suggestions: buildFollowUpSuggestions(question, []) })
    finishAssistant(assistantId, errorText)
    return
  }

  if (charts.length) {
    const answer = "AI文字分析暂时不可用，已先展示统计图表。请稍后重新提问，或换一个更具体的问题。"
    const insights = buildChartInsights(charts)
    const suggestions = buildFollowUpSuggestions(question, charts)
    patchAssistant(assistantId, {
      digesting: false,
      steps: [`正在理解：${question}`, "AI文字分析暂时不可用", `右侧已生成 ${charts.length} 个数据图表`],
      insights,
      suggestions,
      allowFollowUp: true,
    })
    updateDynamicPanels({ question, answer, charts, insights, suggestions })
    streamAssistantText(assistantId, answer, charts)
    return
  }

  const errorText = getFriendlyErrorMessage(answerResult.reason, "AI问答暂时不可用，请稍后再试，或换一个更具体的问题。")
  updateDynamicPanels({ question, answer: errorText, charts: [], insights: [], suggestions: buildFollowUpSuggestions(question, []) })
  finishAssistant(assistantId, errorText)
}

function extractAiAnswer(data) {
  if (typeof data === "string") return data.trim()
  const candidates = [
    data?.answer,
    data?.text,
    data?.content,
    data?.reply,
    data?.message?.content,
    data?.choices?.[0]?.message?.content,
  ]
  return candidates.find((item) => typeof item === "string" && item.trim())?.trim() || ""
}

function isAiFailureAnswer(answer) {
  return /调用\s*(DeepSeek|AI|模型).*失败|未解析到回答内容|AI接口没有返回有效回答|服务异常|请求失败/i.test(String(answer || ""))
}

async function handleBalanceQuestion(question, assistantId) {
  patchAssistant(assistantId, {
    digestText: "正在检索草畜平衡对象",
  })

  try {
    const targets = await queryBalanceTargets(question)
    const normalizedTargets = normalizeBalanceTargets(targets)
    if (!normalizedTargets.length) {
      patchAssistant(assistantId, {
        digesting: false,
        steps: [`正在理解：${question}`, "未匹配到草畜平衡对象"],
      })
      streamAssistantText(
        assistantId,
        "暂时没有匹配到对应的区域或牧户。可以输入更明确的乡镇、村名、牧户或牧场名称，例如“红原县草畜平衡建议”。",
      )
      return
    }

    if (normalizedTargets.length > 1) {
      patchAssistant(assistantId, {
        digesting: false,
        steps: [`正在理解：${question}`, "已匹配多个对象，请选择一个继续"],
        choices: normalizedTargets.slice(0, 8),
      })
      loading.value = false
      return
    }

    await requestBalanceAdvice(normalizedTargets[0], assistantId)
  } catch (error) {
    finishAssistant(assistantId, getFriendlyErrorMessage(error, "草畜平衡对象检索失败，请稍后再试。"))
  }
}

async function selectBalanceChoice(choice) {
  if (loading.value) return
  const question = choice.name || choice.label || "草畜平衡建议"
  const userMessage = {
    id: createId(),
    role: "user",
    text: question,
  }
  const assistantMessage = {
    id: createId(),
    role: "assistant",
    steps: [`已选择：${question}`],
    digesting: true,
    digestText: "正在消化草畜平衡数据",
    visibleText: "",
    fullText: "",
    streaming: false,
    choices: [],
    charts: [],
    insights: [],
    suggestions: [],
  }
  messages.value = [...messages.value, userMessage, assistantMessage]
  loading.value = true
  await scrollToBottom()
  await requestBalanceAdvice(choice, assistantMessage.id)
}

async function requestBalanceAdvice(choice, assistantId) {
  const name = choice?.name || choice?.label || "该区域"
  const params = getBalanceTargetParams(choice)
  patchAssistant(assistantId, {
    steps: [`已锁定：${name}`, "正在消化草畜平衡数据"],
    digestText: "正在生成平衡建议",
  })

  try {
    const data = await getLivestockBalance(params)
    const answer = formatBalanceAdvice(name, data || {})
    patchAssistant(assistantId, {
      digesting: false,
      steps: [`已锁定：${name}`, "已生成草畜平衡建议"],
    })
    updateBalancePanels(name, data || {}, answer)
    streamAssistantText(assistantId, answer)
  } catch (error) {
    finishAssistant(assistantId, getFriendlyErrorMessage(error, "小牧刚才没有消化成功，请稍后再问一次。"))
  }
}

function shouldUseBalanceFlow(question) {
  return /草畜|平衡|承载|载畜|超载|放牧压力|轮牧|休牧|牧场建议|牧户建议/.test(question)
}

function normalizeBalanceTargets(rows = []) {
  return rows
    .map((item, index) => ({
      key: item?.key || item?.type || "area_code",
      value: item?.value || item?.id || item?.code || item?.areaCode || item?.pasturesId,
      name: item?.name || item?.label || item?.areaName || item?.pasturesName || `对象${index + 1}`,
      raw: item,
    }))
    .filter((item) => item.value || item.name === "红原县")
}

function getBalanceTargetParams(choice) {
  if (choice?.key === "pasturesId" || choice?.key === "pastures_id") {
    return { pasturesId: choice.value }
  }
  return { areaCode: choice?.value || "513233" }
}

function formatBalanceAdvice(name, result = {}) {
  const areaRange = pick(result, ["areaRange", "arearange", "area_range", "area", "acreage"], 0)
  const yakNumber = pick(result, ["yakNumber", "yaknumber", "yak_number", "livestock_capacity", "livestockCapacity", "livestockcapacity"], 0)
  const overloadRate = pick(result, ["overloadRate", "overloadrate", "overload_rate"], "")
  const risk = overloadRate === "" ? null : getRiskLevel(overloadRate)
  const level = risk?.label || pick(result, ["level", "conclusion"], "暂无等级")
  const advice = pick(result, ["advice", "balanceAdvice", "balanceadvice", "balance_advice"], "暂无建议，请稍后再试。")

  return `**${name}草畜平衡建议**

- 草地面积：${formatNumber(areaRange)} 亩
- 牦牛/载畜量：${formatNumber(yakNumber)} 头
- 草畜平衡等级：${level}
- 超载率：${formatNumber(overloadRate === "" ? 0 : overloadRate)}%

**建议**
${advice}`
}

function pick(source, keys, fallback = "") {
  for (const key of keys) {
    const value = source?.[key]
    if (value !== undefined && value !== null && value !== "") return value
  }
  return fallback
}

function formatNumber(value) {
  const number = Number(String(value).replace(/,/g, ""))
  if (!Number.isFinite(number)) return value || "--"
  return number.toLocaleString("zh-CN", { maximumFractionDigits: 2 })
}

function extractAiPanels(data = {}) {
  const panelPayload = data?.panels || data?.sidePanels || data?.layout || {}
  return {
    leftPanels: normalizePanelList(data?.leftPanels || panelPayload?.leftPanels || panelPayload?.left || []),
    rightPanels: normalizePanelList(data?.rightPanels || panelPayload?.rightPanels || panelPayload?.right || []),
  }
}

function normalizePanelList(panels = []) {
  const list = Array.isArray(panels) ? panels : []
  return list
    .map((panel, index) => {
      const type = normalizePanelType(panel?.type)
      const chart = panel?.chart || panel?.data?.chart || null
      return {
        id: panel?.id || `ai-panel-${Date.now()}-${index}`,
        type,
        kicker: panel?.kicker || panel?.category || (type === "chart" ? "图表" : "AI洞察"),
        title: panel?.title || panel?.name || "动态内容",
        badge: panel?.badge || panel?.unit || "",
        text: panel?.text || panel?.content || panel?.summary || "",
        chart,
        items: normalizePanelItems(panel?.items || panel?.metrics || panel?.data?.items),
        actions: normalizePanelActions(panel?.actions || panel?.suggestions),
      }
    })
    .filter((panel) => panel.title && !isFollowUpPanel(panel) && (panel.text || panel.chart || panel.items.length || panel.actions.length))
}

function isFollowUpPanel(panel = {}) {
  const text = `${panel.kicker || ""}${panel.title || ""}${panel.type || ""}`
  return text.includes("\u7ee7\u7eed\u8ffd\u95ee") || text.includes("\u5173\u8054\u95ee\u9898") || /suggestions/i.test(text)
}

function normalizePanelType(type) {
  if (["chart", "metrics", "metric", "actions", "suggestions", "text"].includes(type)) return type === "metric" ? "metrics" : type
  return "text"
}

function normalizePanelItems(items = []) {
  const list = Array.isArray(items) ? items : []
  return list
    .map((item, index) => ({
      label: item?.label || item?.name || `指标${index + 1}`,
      value: item?.value ?? item?.count ?? item?.number ?? "--",
      note: item?.note || item?.desc || item?.description || "",
    }))
    .filter((item) => item.label)
}

function normalizePanelActions(actions = []) {
  const list = Array.isArray(actions) ? actions : []
  return list
    .map((action) => {
      const question = action?.question || action?.value || action?.text || action?.label
      return {
        label: action?.label || action?.name || question,
        question,
      }
    })
    .filter((action) => action.label && action.question)
    .slice(0, 4)
}

function updateDynamicPanels({ question, answer, charts = [], insights = [], suggestions = [], aiPanels } = {}) {
  const aiLeft = aiPanels?.leftPanels || []
  const aiRight = aiPanels?.rightPanels || []
  if (aiLeft.length || aiRight.length) {
    leftPanels.value = aiLeft
    rightPanels.value = aiRight
    return
  }

  const fallbackPanels = buildFallbackPanels({ question, answer, charts, insights, suggestions })
  leftPanels.value = fallbackPanels.left
  rightPanels.value = fallbackPanels.right
}

function buildFallbackPanels({ question, answer, charts = [], insights = [], suggestions = [] } = {}) {
  const left = []
  const right = []
  const metricItems = (insights.length ? insights : buildChartInsights(charts)).map((item) => ({
    label: item.label,
    value: item.value,
    note: item.note,
  }))

  if (metricItems.length) {
    left.push({
      id: `summary-${createId()}`,
      type: "metrics",
      kicker: "AI洞察",
      title: "数据摘要",
      badge: "实时",
      items: metricItems.slice(0, 4),
      actions: [],
    })
  } else if (answer) {
    left.push({
      id: `summary-${createId()}`,
      type: "text",
      kicker: "AI洞察",
      title: "回答要点",
      badge: "",
      text: extractAnswerBrief(answer),
      items: [],
      actions: [],
    })
  }

  charts.slice(0, 2).forEach((chart, index) => {
    right.push({
      id: `chart-${chart.id || index}-${createId()}`,
      type: "chart",
      kicker: index === 0 ? "可视分析" : "关联图表",
      title: chart.title || `统计图表${index + 1}`,
      badge: chart.unit || "",
      chart,
      items: [],
      actions: [],
      text: "",
    })
  })

  if (!right.length && answer) {
    right.push({
      id: `answer-${createId()}`,
      type: "text",
      kicker: "AI回答",
      title: String(question || "问题分析").slice(0, 16),
      badge: "模型",
      text: extractAnswerBrief(answer, 140),
      items: [],
      actions: [],
    })
  }

  return {
    left: left.slice(0, 2),
    right: right.slice(0, 2),
  }
}

function updateBalancePanels(name, result = {}, answer = "") {
  const areaRange = pick(result, ["areaRange", "arearange", "area_range", "area", "acreage"], 0)
  const yakNumber = pick(result, ["yakNumber", "yaknumber", "yak_number", "livestock_capacity", "livestockCapacity", "livestockcapacity"], 0)
  const overloadRate = pick(result, ["overloadRate", "overloadrate", "overload_rate"], "")
  const risk = overloadRate === "" ? null : getRiskLevel(overloadRate)
  const level = risk?.label || pick(result, ["level", "conclusion"], "暂无等级")

  leftPanels.value = [
    {
      id: `balance-${createId()}`,
      type: "metrics",
      kicker: "草畜平衡",
      title: name,
      badge: level,
      items: [
        { label: "草地面积", value: `${formatNumber(areaRange)}亩`, note: "承载基础" },
        { label: "牦牛/载畜量", value: `${formatNumber(yakNumber)}头`, note: "当前数据" },
        { label: "超载率", value: `${formatNumber(overloadRate === "" ? 0 : overloadRate)}%`, note: level },
      ],
      actions: [],
    },
  ]
  rightPanels.value = [
    {
      id: `balance-advice-${createId()}`,
      type: "text",
      kicker: "AI建议",
      title: "管理建议",
      badge: "模型",
      text: extractAnswerBrief(answer, 180),
      items: [],
      actions: normalizePanelActions([
        { label: "看牦牛分布", question: "牦牛分布" },
        { label: "看草地面积", question: "草地面积分布" },
      ]),
    },
  ]
}

function extractAnswerBrief(answer, limit = 120) {
  const text = String(answer || "")
    .replace(/\*\*/g, "")
    .replace(/^#+\s*/gm, "")
    .split(/\r?\n/)
    .map((line) => line.replace(/^[-*]\s+/, "").trim())
    .filter(Boolean)
    .slice(0, 4)
    .join("\n")
  return text.length > limit ? `${text.slice(0, limit)}...` : text
}

function buildChartInsights(charts = []) {
  const insights = []
  charts.slice(0, 2).forEach((chart) => {
    const rows = (chart.rows || [])
      .map((row) => ({
        ...row,
        value: Number(row.value),
      }))
      .filter((row) => row.name && Number.isFinite(row.value))
    if (!rows.length) return

    const unit = chart.unit || rows.find((row) => row.unit)?.unit || ""
    const sorted = rows.slice().sort((a, b) => b.value - a.value)
    const total = rows.reduce((sum, row) => sum + row.value, 0)
    const average = total / rows.length
    const top = sorted[0]
    const bottom = sorted[sorted.length - 1]

    insights.push({
      label: "最高",
      value: `${top.name} ${formatNumber(top.value)}${unit}`,
      note: chart.title,
    })
    if (rows.length > 2) {
      insights.push({
        label: chart.type === "pie" ? "占比项" : "最低",
        value: `${bottom.name} ${formatNumber(bottom.value)}${unit}`,
        note: chart.type === "pie" ? "最小结构项" : "低值区域",
      })
    }
    insights.push({
      label: "合计",
      value: `${formatNumber(total)}${unit}`,
      note: rows.length > 1 ? `覆盖 ${rows.length} 项` : chart.title,
    })
    if (chart.type !== "pie" && rows.length > 1) {
      insights.push({
        label: "均值",
        value: `${formatNumber(average)}${unit}`,
        note: "按当前图表项计算",
      })
    }
  })
  return insights.slice(0, 4)
}

function buildFollowUpSuggestions(question, charts = []) {
  return []
}

function patchAssistant(id, patch, shouldScroll = true) {
  messages.value = messages.value.map((message) => (message.id === id ? { ...message, ...patch } : message))
  if (shouldScroll) scrollToBottom()
}

function finishAssistant(id, text) {
  patchAssistant(id, {
    digesting: false,
    steps: ["请求已结束"],
  })
  streamAssistantText(id, text)
}

function getFriendlyErrorMessage(error, fallback) {
  const message = String(error?.message || "")
  if (error?.code === 401 || error?.code === "401" || error?.code === "HTTP_401" || /未登录|授权|Unauthorized|HTTP 401/i.test(message)) {
    return "接口需要登录授权。当前智慧管理不会使用假数据，请先完成平台登录，或继续使用业务统计数据问数。"
  }
  if (error?.code === "REQUEST_TIMEOUT" || /timeout|timed out|超时/i.test(message)) {
    return "服务响应时间较长，请稍后再试，或换一个更具体的问题。"
  }
  if (error?.code === "REQUEST_ABORTED" || /aborted|abort|signal/i.test(message)) {
    return "本次请求被中断了，请重新发送一次。"
  }
  if (/Failed to fetch|NetworkError|Load failed|HTTP 50|HTTP 40/i.test(message)) {
    return fallback
  }
  return /[a-zA-Z]{4,}/.test(message) ? fallback : message || fallback
}

function streamAssistantText(id, text) {
  stopStreaming()
  const source = normalizeYakText(text)
  const chunkSize = source.length > 1200 ? 36 : source.length > 700 ? 24 : 14
  let index = 0
  patchAssistant(id, {
    fullText: source,
    visibleText: "",
    streaming: true,
    choices: [],
    charts: [],
  })

  const tick = () => {
    index = Math.min(index + chunkSize, source.length)
    patchAssistant(id, {
      visibleText: source.slice(0, index),
      streaming: index < source.length,
    })

    if (index >= source.length) {
      streamTimer = null
      loading.value = false
      return
    }
    streamTimer = window.setTimeout(tick, 18)
  }

  streamTimer = window.setTimeout(tick, 0)
}

function stopStreaming() {
  if (streamTimer) {
    window.clearTimeout(streamTimer)
    streamTimer = null
  }
}

function renderMarkdown(value) {
  const lines = escapeHtml(value).split(/\r?\n/)
  const html = []
  let listItems = []
  let ordered = false

  const flushList = () => {
    if (!listItems.length) return
    html.push(`<${ordered ? "ol" : "ul"}>${listItems.map((item) => `<li>${renderInline(item)}</li>`).join("")}</${ordered ? "ol" : "ul"}>`)
    listItems = []
    ordered = false
  }

  lines.forEach((line) => {
    const trimmed = line.trim()
    if (!trimmed) {
      flushList()
      return
    }

    const heading = trimmed.match(/^#{1,4}\s+(.+)$/) || trimmed.match(/^\*\*(.+)\*\*$/)
    if (heading) {
      flushList()
      html.push(`<h3>${renderInline(heading[1])}</h3>`)
      return
    }

    const orderedMatch = trimmed.match(/^\d+[.)、]\s+(.+)$/)
    const unorderedMatch = trimmed.match(/^[-*]\s+(.+)$/)
    if (orderedMatch || unorderedMatch) {
      const nextOrdered = Boolean(orderedMatch)
      if (listItems.length && ordered !== nextOrdered) flushList()
      ordered = nextOrdered
      listItems.push(orderedMatch ? orderedMatch[1] : unorderedMatch[1])
      return
    }

    flushList()
    html.push(`<p>${renderInline(trimmed)}</p>`)
  })

  flushList()
  return html.join("")
}

function renderInline(value) {
  return value.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
}

function escapeHtml(value) {
  return String(value || "")
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;")
}

async function scrollToBottom() {
  await nextTick()
  window.requestAnimationFrame(() => {
    if (dialogueRef.value) {
      dialogueRef.value.scrollTop = dialogueRef.value.scrollHeight
    }
  })
}

</script>

<style lang="scss">
.wisdom-management {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  color: #dffaff;
  background:
    radial-gradient(circle at 50% 45%, rgba(37, 161, 133, 0.28), rgba(3, 16, 28, 0.18) 35%, rgba(2, 8, 18, 0.86) 72%),
    linear-gradient(180deg, #041324, #030914);
  pointer-events: auto;
}

.wisdom-backdrop {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.wisdom-grid {
  position: absolute;
  left: 430px;
  right: 430px;
  top: 128px;
  bottom: 8px;
  opacity: 0.18;
  background-image:
    linear-gradient(rgba(48, 220, 255, 0.14) 1px, transparent 1px),
    linear-gradient(90deg, rgba(48, 220, 255, 0.14) 1px, transparent 1px);
  background-size: 44px 44px;
  mask-image: radial-gradient(circle at center, #000 42%, transparent 78%);
}

.wisdom-orbit {
  position: absolute;
  left: 50%;
  top: 50%;
  border: 1px solid rgba(48, 220, 255, 0.18);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  box-shadow: inset 0 0 70px rgba(48, 220, 255, 0.06), 0 0 70px rgba(32, 232, 188, 0.08);
}

.wisdom-orbit-a {
  width: 760px;
  height: 760px;
  animation: wisdomOrbitSpin 24s linear infinite;
}

.wisdom-orbit-b {
  width: 980px;
  height: 260px;
  border-color: rgba(250, 211, 107, 0.16);
  transform: translate(-50%, -48%) rotate(-9deg);
  animation: wisdomPulse 6s ease-in-out infinite;
}

.wisdom-scan {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 620px;
  height: 620px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: conic-gradient(from 0deg, transparent, rgba(48, 220, 255, 0.2), transparent 24%);
  opacity: 0.2;
  animation: wisdomScanSpin 8s linear infinite;
}

.wisdom-side {
  position: absolute;
  top: 112px;
  bottom: 18px;
  z-index: 5;
  width: clamp(390px, 21vw, 460px);
  overflow-y: auto;
  display: grid;
  align-content: start;
  gap: 16px;
  padding: 6px 0 10px;
  box-sizing: border-box;
  scrollbar-width: none;
}

.wisdom-side::-webkit-scrollbar {
  width: 0;
  height: 0;
}

.wisdom-side-left {
  left: clamp(24px, 2.4vw, 52px);
}

.wisdom-side-right {
  right: clamp(24px, 2.4vw, 52px);
}

.wisdom-side-panel {
  min-height: 0;
  border: 1px solid rgba(48, 220, 255, 0.22);
  background:
    linear-gradient(135deg, rgba(6, 37, 50, 0.9), rgba(4, 19, 32, 0.86)),
    radial-gradient(circle at 18% 0%, rgba(39, 240, 192, 0.12), transparent 34%);
  box-shadow: inset 0 0 24px rgba(48, 220, 255, 0.07), 0 14px 28px rgba(0, 0, 0, 0.22);
  padding: 15px;
  box-sizing: border-box;
}

.wisdom-side-panel.is-chart {
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
}

.wisdom-side-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.wisdom-side-header span {
  display: block;
  margin-bottom: 5px;
  color: rgba(48, 220, 255, 0.72);
  font-size: 12px;
}

.wisdom-side-header strong {
  display: block;
  max-width: 220px;
  color: #fff;
  font-size: 17px;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wisdom-side-header em {
  flex: 0 0 auto;
  padding: 3px 8px;
  border: 1px solid rgba(255, 215, 108, 0.32);
  color: #ffd76c;
  font-size: 12px;
  font-style: normal;
  background: rgba(255, 215, 108, 0.08);
}

.wisdom-side-metrics {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 9px;
}

.wisdom-side-metric {
  min-width: 0;
  padding: 10px;
  border: 1px solid rgba(48, 220, 255, 0.16);
  background: rgba(3, 22, 35, 0.58);
  box-sizing: border-box;
}

.wisdom-side-metric span,
.wisdom-side-metric em {
  display: block;
  color: rgba(223, 250, 255, 0.55);
  font-size: 12px;
  font-style: normal;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wisdom-side-metric strong {
  display: block;
  margin: 4px 0;
  color: #ffd76c;
  font-family: "D-DIN", Arial, sans-serif;
  font-size: 17px;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wisdom-side-text {
  color: rgba(237, 253, 255, 0.88);
  font-size: 13px;
  line-height: 1.7;
}

.wisdom-side-text h3 {
  margin: 0 0 8px;
  color: #fff;
  font-size: 15px;
}

.wisdom-side-text p {
  margin: 7px 0;
}

.wisdom-side-text ul,
.wisdom-side-text ol {
  margin: 8px 0 8px 18px;
  padding: 0;
}

.wisdom-side-text strong {
  color: #ffd76c;
}

.wisdom-side-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.wisdom-side-text + .wisdom-side-actions,
.wisdom-side-metrics + .wisdom-side-actions {
  margin-top: 12px;
}

.wisdom-side-actions button {
  height: 30px;
  min-width: 84px;
  padding: 0 10px;
  border: 1px solid rgba(48, 220, 255, 0.28);
  background: linear-gradient(135deg, rgba(8, 48, 62, 0.76), rgba(6, 28, 43, 0.76));
  color: rgba(255, 215, 108, 0.9);
  font-size: 12px;
  cursor: pointer;
}

.wisdom-side-actions button:hover:not(:disabled) {
  border-color: rgba(255, 215, 108, 0.62);
  color: #fff;
}

.wisdom-side-chart.smart-chart-card {
  width: 100%;
  height: 348px;
  margin-top: 0;
}

.wisdom-side-chart .smart-chart-header {
  height: 58px;
  padding: 10px 13px 0;
}

.wisdom-side-chart .smart-chart-header strong {
  font-size: 15px;
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wisdom-side-chart .smart-chart-header span,
.wisdom-side-chart .smart-chart-header em {
  font-size: 11px;
}

.wisdom-side-chart .smart-chart {
  height: 264px;
}

.wisdom-side-chart .smart-chart-source {
  height: 24px;
  padding: 0 13px;
}

.wisdom-avatar-ring,
.wisdom-empty-mark span {
  position: absolute;
  inset: 0;
  border: 1px solid rgba(48, 220, 255, 0.62);
  border-radius: 50%;
  animation: wisdomSpin 7s linear infinite;
}

.wisdom-avatar-ring::before,
.wisdom-empty-mark span::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #ffd76c;
  box-shadow: 0 0 14px rgba(255, 215, 108, 0.8);
}

.wisdom-chat {
  position: absolute;
  left: 50%;
  top: 112px;
  bottom: 18px;
  z-index: 4;
  width: clamp(720px, calc(100% - 1040px), 900px);
  display: flex;
  flex-direction: column;
  min-width: 0;
  transform: translateX(-50%);
}

.wisdom-dialogue {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 8px 8px 18px;
  scrollbar-width: thin;
  scrollbar-color: rgba(48, 220, 255, 0.5) rgba(3, 18, 30, 0.3);
}

.wisdom-dialogue.is-empty {
  display: grid;
  place-items: center;
}

.wisdom-empty-state {
  position: relative;
  width: 560px;
  min-height: 360px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.wisdom-empty-mark {
  position: relative;
  width: 126px;
  height: 126px;
  display: grid;
  place-items: center;
}

.wisdom-empty-mark strong {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #071520;
  font-family: "D-DIN", Arial, sans-serif;
  font-size: 34px;
  background: radial-gradient(circle at 32% 24%, #fff, #9cf8e9 48%, #2bd4cb 100%);
  box-shadow: 0 0 42px rgba(78, 243, 222, 0.38);
}

.wisdom-empty-state h2 {
  margin: 18px 0 10px;
  color: #fff;
  font-size: 28px;
  letter-spacing: 0;
}

.wisdom-empty-state p {
  margin: 0;
  max-width: 420px;
  color: rgba(223, 250, 255, 0.68);
  font-size: 15px;
  line-height: 1.8;
}

.wisdom-prompt-grid {
  width: min(520px, 100%);
  margin-top: 22px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

.wisdom-prompt-grid button,
.wisdom-suggestion-list button {
  height: 34px;
  border: 1px solid rgba(48, 220, 255, 0.28);
  background: linear-gradient(135deg, rgba(8, 48, 62, 0.78), rgba(6, 28, 43, 0.78));
  color: rgba(223, 250, 255, 0.86);
  font-size: 13px;
  cursor: pointer;
  box-shadow: inset 0 0 16px rgba(48, 220, 255, 0.06);
}

.wisdom-prompt-grid button:hover:not(:disabled),
.wisdom-suggestion-list button:hover:not(:disabled) {
  border-color: rgba(255, 215, 108, 0.62);
  color: #fff;
  background: linear-gradient(135deg, rgba(48, 220, 255, 0.18), rgba(255, 215, 108, 0.12));
}

.wisdom-message {
  display: flex;
  margin: 14px 0;
  gap: 10px;
}

.wisdom-message.is-user {
  justify-content: flex-end;
}

.wisdom-message-mark {
  flex: 0 0 36px;
  height: 36px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #06151f;
  font-weight: 800;
  background: linear-gradient(135deg, #dffaff, #4cf0de);
  box-shadow: 0 0 22px rgba(78, 243, 222, 0.36);
}

.wisdom-bubble {
  max-width: min(680px, calc(100% - 48px));
  box-sizing: border-box;
  border: 1px solid rgba(48, 220, 255, 0.18);
  background: rgba(4, 22, 34, 0.92);
  box-shadow: inset 0 0 24px rgba(48, 220, 255, 0.06), 0 14px 34px rgba(0, 0, 0, 0.34);
  color: rgba(237, 253, 255, 0.92);
  font-size: 14px;
  line-height: 1.7;
  padding: 13px 15px;
}

.smart-chart-loading {
  width: min(620px, 100%);
  height: 292px;
  margin-top: 12px;
  display: grid;
  place-items: center;
  box-sizing: border-box;
  border: 1px solid rgba(48, 220, 255, 0.22);
  background: rgba(4, 22, 34, 0.72);
  color: rgba(255, 215, 108, 0.86);
  font-size: 13px;
  box-shadow: inset 0 0 24px rgba(48, 220, 255, 0.06);
}

.wisdom-suggestion-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.wisdom-suggestion-list button {
  width: auto;
  min-width: 86px;
  padding: 0 12px;
  color: rgba(255, 215, 108, 0.9);
}

.wisdom-message.is-user .wisdom-bubble {
  max-width: min(560px, 76%);
  border-color: rgba(255, 215, 108, 0.26);
  background: linear-gradient(135deg, rgba(50, 115, 106, 0.9), rgba(13, 54, 65, 0.86));
}

.wisdom-steps {
  display: grid;
  gap: 8px;
  margin-bottom: 10px;
}

.wisdom-step {
  display: flex;
  align-items: center;
  gap: 8px;
  color: rgba(223, 250, 255, 0.64);
  font-size: 13px;
}

.wisdom-step span {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid rgba(48, 220, 255, 0.7);
  position: relative;
}

.wisdom-step span::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 2px;
  width: 4px;
  height: 7px;
  border-right: 2px solid #30dcff;
  border-bottom: 2px solid #30dcff;
  transform: rotate(40deg);
}

.wisdom-digesting {
  display: flex;
  align-items: center;
  gap: 8px;
  color: rgba(255, 215, 108, 0.88);
}

.wisdom-digesting span,
.wisdom-button-loading {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #ffd76c;
  animation: wisdomBounce 1s ease-in-out infinite;
}

.wisdom-digesting span:nth-child(2) {
  animation-delay: 0.14s;
}

.wisdom-digesting span:nth-child(3) {
  animation-delay: 0.28s;
}

.wisdom-digesting em {
  font-style: normal;
}

.wisdom-answer {
  color: rgba(237, 253, 255, 0.94);
}

.wisdom-answer h3 {
  margin: 12px 0 8px;
  color: #fff;
  font-size: 17px;
}

.wisdom-answer p {
  margin: 8px 0;
}

.wisdom-answer ul,
.wisdom-answer ol {
  margin: 8px 0 8px 20px;
  padding: 0;
}

.wisdom-answer li {
  margin: 4px 0;
}

.wisdom-answer strong {
  color: #ffd76c;
}

.wisdom-stream-caret {
  display: inline-block;
  width: 8px;
  height: 18px;
  margin-left: 4px;
  vertical-align: middle;
  background: #30dcff;
  animation: wisdomBlink 0.8s steps(2, start) infinite;
}

.wisdom-choice-list {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
}

.wisdom-choice-list button {
  min-width: 96px;
  height: 34px;
  padding: 0 14px;
  border: 1px solid rgba(48, 220, 255, 0.32);
  background: rgba(9, 48, 62, 0.74);
  color: #dffaff;
  cursor: pointer;
}

.wisdom-choice-list button:hover:not(:disabled) {
  border-color: rgba(255, 215, 108, 0.62);
  color: #fff;
}

.wisdom-input-bar {
  flex: 0 0 64px;
  padding: 8px 8px 0;
  box-sizing: border-box;
}

.wisdom-input-shell {
  height: 50px;
  border: 1px solid rgba(48, 220, 255, 0.34);
  background: linear-gradient(135deg, rgba(8, 42, 55, 0.92), rgba(3, 18, 31, 0.92));
  box-shadow: inset 0 0 24px rgba(48, 220, 255, 0.08), 0 0 30px rgba(48, 220, 255, 0.1);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 8px 0 16px;
  box-sizing: border-box;
}

.wisdom-input-icon {
  width: 16px;
  height: 16px;
  border: 2px solid #30dcff;
  border-radius: 50%;
  position: relative;
  flex: 0 0 auto;
}

.wisdom-input-icon::after {
  content: "";
  position: absolute;
  right: -7px;
  bottom: -5px;
  width: 8px;
  height: 2px;
  border-radius: 2px;
  background: #30dcff;
  transform: rotate(45deg);
}

.wisdom-input-shell input {
  min-width: 0;
  flex: 1;
  border: 0;
  outline: 0;
  background: transparent;
  color: #fff;
  font-size: 15px;
}

.wisdom-input-shell input::placeholder {
  color: rgba(223, 250, 255, 0.42);
}

.wisdom-submit-button {
  width: 92px;
  height: 36px;
  border: 1px solid rgba(255, 215, 108, 0.46);
  background: linear-gradient(135deg, rgba(255, 215, 108, 0.26), rgba(48, 220, 255, 0.12));
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  cursor: pointer;
}

.wisdom-voice-button {
  position: relative;
  flex: 0 0 auto;
  width: 38px;
  height: 36px;
  border: 1px solid rgba(48, 220, 255, 0.32);
  background: rgba(6, 35, 49, 0.76);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.wisdom-voice-button span {
  position: relative;
  width: 10px;
  height: 17px;
  border: 2px solid #30dcff;
  border-radius: 8px;
  box-sizing: border-box;
}

.wisdom-voice-button span::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -8px;
  width: 14px;
  height: 8px;
  border: 2px solid #30dcff;
  border-top: 0;
  border-radius: 0 0 10px 10px;
  transform: translateX(-50%);
}

.wisdom-voice-button span::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -12px;
  width: 12px;
  height: 2px;
  border-radius: 2px;
  background: #30dcff;
  transform: translateX(-50%);
}

.wisdom-voice-button.is-listening {
  border-color: rgba(255, 215, 108, 0.62);
  background: rgba(255, 215, 108, 0.14);
  box-shadow: 0 0 18px rgba(255, 215, 108, 0.22);
}

.wisdom-voice-button.is-listening span,
.wisdom-voice-button.is-listening span::before {
  border-color: #ffd76c;
}

.wisdom-voice-button.is-listening span::after {
  background: #ffd76c;
}

.wisdom-submit-button:disabled,
.wisdom-voice-button:disabled {
  cursor: not-allowed;
  opacity: 0.62;
}

.wisdom-button-loading {
  width: 12px;
  height: 12px;
  border: 2px solid rgba(255, 255, 255, 0.34);
  border-top-color: #fff;
  background: transparent;
  animation: wisdomSpin 0.8s linear infinite;
}

@keyframes wisdomSpin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes wisdomOrbitSpin {
  to {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}

@keyframes wisdomScanSpin {
  to {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}

@keyframes wisdomPulse {
  0%,
  100% {
    opacity: 0.36;
  }
  50% {
    opacity: 0.72;
  }
}

@keyframes wisdomBounce {
  0%,
  100% {
    transform: translateY(0);
    opacity: 0.42;
  }
  50% {
    transform: translateY(-5px);
    opacity: 1;
  }
}

@keyframes wisdomBlink {
  50% {
    opacity: 0;
  }
}

@media (max-width: 1480px) {
  .wisdom-side {
    top: 112px;
    bottom: 16px;
    width: 318px;
  }

  .wisdom-side-left {
    left: 20px;
  }

  .wisdom-side-right {
    right: 20px;
  }

  .wisdom-side-chart.smart-chart-card {
    height: 320px;
  }

  .wisdom-side-chart .smart-chart {
    height: 236px;
  }

  .wisdom-chat {
    width: clamp(560px, calc(100% - 760px), 760px);
  }
}

@media (max-width: 1280px) {
  .wisdom-side {
    display: none;
  }

  .wisdom-chat {
    width: min(860px, calc(100% - 80px));
  }
}
</style>
