<script setup lang="ts">
import type { Attachment, ChatMessage, ReasoningStep } from './types';

/**
 * 消息气泡组件 - ChatGPT 风格
 */
import { computed, ref, watch } from 'vue';

import {
  AlertCircle,
  Bot,
  ChevronDown,
  ChevronRight,
  Copy,
  FileText,
  Image,
  Loader2,
  RefreshCw,
  ThumbsDown,
  ThumbsUp,
  Volume2,
} from '@vben/icons';
import { useUserStore } from '@vben/stores';

import { ElMessage } from 'element-plus';

import { UserAvatar } from '#/components/user-avatar';

import { renderMarkdown } from './markdown';

const props = withDefaults(
  defineProps<{
    assistantAvatar?: string;
    assistantName?: string;
    /** 预加载的用户头像 URL（避免每条消息重复加载） */
    cachedUserAvatarUrl?: string;
    /** 当前用户信息（从父组件传入，避免重复获取） */
    currentUser?: null | {
      avatar?: string;
      realName?: string;
      username?: string;
    };
    enableFeedback?: boolean;
    enableRegenerate?: boolean;
    isLast?: boolean;
    message: ChatMessage;
    showAvatar?: boolean;
    showReasoningSteps?: boolean;
    userAvatar?: string;
  }>(),
  {
    showAvatar: true,
    enableFeedback: false,
    enableRegenerate: false,
    showReasoningSteps: true,
    isLast: false,
  },
);

const emit = defineEmits<{
  (e: 'regenerate', messageId: string): void;
  (e: 'feedback', messageId: string, feedback: 'dislike' | 'like'): void;
  (e: 'copy', content: string): void;
  (e: 'preview', attachment: Attachment): void;
  (e: 'interaction-submit', messageId: string, value: any): void;
  (e: 'interaction-cancel', messageId: string): void;
}>();

// 交互输入值
const interactionInputValue = ref('');
const interactionChoiceValue = ref<string | string[]>('');

// 多选切换
const toggleMultiChoice = (value: string) => {
  const arr = interactionChoiceValue.value as string[];
  const idx = arr.indexOf(value);
  interactionChoiceValue.value =
    idx === -1 ? [...arr, value] : arr.filter((v) => v !== value);
};

const isUser = computed(() => props.message.role === 'user');
const isPending = computed(() => props.message.status === 'pending');
const isFailed = computed(() => props.message.status === 'failed');
const isCompleted = computed(() => props.message.status === 'completed');

// 使用从父组件传入的用户信息（避免重复获取）
// 如果没有传入，则从 store 获取（向后兼容）
const userStore = useUserStore();
const effectiveCurrentUser = computed(
  () => props.currentUser ?? userStore.userInfo,
);

// 渲染 Markdown 内容
const renderedContent = computed(() => {
  if (!props.message.content) return '';
  // 用户消息不渲染 Markdown
  if (isUser.value) return props.message.content;
  // 助手消息渲染 Markdown
  return renderMarkdown(props.message.content);
});

// 推理步骤展开状态：执行中默认展开，完成后默认折叠
const stepsExpanded = ref(true);
const hasManuallyToggled = ref(false);

// 当消息状态变为完成时，自动折叠（除非用户手动展开过）
watch(
  () => props.message.status,
  (newStatus) => {
    if (newStatus === 'completed' && !hasManuallyToggled.value) {
      stepsExpanded.value = false;
    }
  },
  { immediate: true },
);

// 手动切换展开状态
const toggleStepsExpanded = () => {
  hasManuallyToggled.value = true;
  stepsExpanded.value = !stepsExpanded.value;
};

// 复制内容
const handleCopy = async () => {
  try {
    await navigator.clipboard.writeText(props.message.content);
    ElMessage.success('已复制到剪贴板');
    emit('copy', props.message.content);
  } catch (error) {
    console.error('复制失败', error);
    ElMessage.error('复制失败，请重试');
  }
};

// 获取步骤类型标签
const getStepLabel = (step: ReasoningStep) => {
  // 如果是节点类型，根据 status 显示不同的标签
  if (step.type === 'node_start' || step.type === 'node_complete') {
    if (step.status === 'running') {
      return '执行中';
    }
    return '已完成';
  }

  const labels: Record<string, string> = {
    thought: '思考',
    action: '执行',
    observation: '观察',
    tool_call: '调用工具',
    tool_result: '工具结果',
    knowledge_retrieval: '知识库检索',
    annotation_reply: '标注回复',
  };
  return labels[step.type] || step.type;
};

// 获取步骤类型颜色
const getStepColor = (step: ReasoningStep) => {
  // 如果是节点类型，根据 status 显示不同的颜色
  if (step.type === 'node_start' || step.type === 'node_complete') {
    if (step.status === 'running') {
      return 'text-blue-500';
    }
    return 'text-emerald-500';
  }

  const colors: Record<string, string> = {
    thought: 'text-blue-500',
    action: 'text-orange-500',
    observation: 'text-green-500',
    tool_call: 'text-purple-500',
    tool_result: 'text-teal-500',
    knowledge_retrieval: 'text-cyan-500',
    annotation_reply: 'text-emerald-500',
  };
  return colors[step.type] || 'text-gray-500';
};

// 格式化文件大小
const formatFileSize = (bytes?: number) => {
  if (!bytes) return '';
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
};

// 获取附件图标
const getAttachmentIcon = (type: Attachment['type']) => {
  switch (type) {
    case 'audio': {
      return Volume2;
    }
    case 'image': {
      return Image;
    }
    default: {
      return FileText;
    }
  }
};

// 语音播放状态
const isPlaying = ref(false);
let audioElement: HTMLAudioElement | null = null;

// 播放/暂停语音
const toggleVoicePlay = () => {
  if (!props.message.voice?.audioUrl) return;

  if (!audioElement) {
    audioElement = new Audio(props.message.voice.audioUrl);
    audioElement.addEventListener('ended', () => {
      isPlaying.value = false;
    });
  }

  if (isPlaying.value) {
    audioElement.pause();
    isPlaying.value = false;
  } else {
    audioElement.play();
    isPlaying.value = true;
  }
};

// 格式化语音时长
const formatVoiceDuration = (seconds: number) => {
  if (seconds < 60) return `${seconds}"`;
  const mins = Math.floor(seconds / 60);
  const secs = seconds % 60;
  return `${mins}'${secs}"`;
};
</script>

<template>
  <div class="chat-message group" :class="{ 'is-user': isUser }">
    <!-- 头像 -->
    <div v-if="showAvatar" class="chat-avatar">
      <!-- 用户头像：优先使用预加载的头像 URL -->
      <img
        v-if="isUser && cachedUserAvatarUrl"
        :src="cachedUserAvatarUrl"
        alt="user"
        class="size-9 rounded-full object-cover"
      />
      <!-- 用户头像：使用 UserAvatar 组件（向后兼容，无预加载时） -->
      <UserAvatar
        v-else-if="isUser && effectiveCurrentUser"
        :name="effectiveCurrentUser.realName || effectiveCurrentUser.username"
        :avatar="effectiveCurrentUser.avatar"
        :size="36"
        :font-size="14"
        :shadow="false"
        :show-popover="false"
      />
      <!-- 用户头像加载中或未登录时显示默认图标 -->
      <div
        v-else-if="isUser"
        class="flex size-9 items-center justify-center rounded-full bg-[var(--el-color-primary)]"
      >
        <User class="size-5 text-white" />
      </div>
      <!-- 助手头像：优先使用自定义头像，否则显示 Bot 图标 -->
      <img
        v-else-if="!isUser && assistantAvatar"
        :src="assistantAvatar"
        alt="assistant"
      />
      <Bot v-else class="size-5" />
    </div>

    <!-- 消息内容区 -->
    <div class="chat-content">
      <!-- 助手名称 -->
      <div v-if="!isUser && assistantName" class="chat-name">
        {{ assistantName }}
      </div>

      <!-- 消息气泡 -->
      <div class="chat-bubble" :class="{ 'is-error': isFailed }">
        <!-- 执行步骤（执行中展开显示，完成后折叠） -->
        <div v-if="message.reasoning_steps?.length" class="bubble-steps">
          <div class="bubble-steps-header" @click="toggleStepsExpanded">
            <component
              :is="stepsExpanded ? ChevronDown : ChevronRight"
              class="size-3"
            />
            <span v-if="isPending" class="steps-status running">执行中...</span>
            <span v-else class="steps-status">执行过程 ({{ message.reasoning_steps.length }} 步)</span>
          </div>
          <div v-if="stepsExpanded" class="bubble-steps-list">
            <div
              v-for="(step, idx) in message.reasoning_steps"
              :key="idx"
              class="bubble-step"
              :class="{
                'is-last':
                  idx === message.reasoning_steps.length - 1 && isPending,
              }"
            >
              <span class="step-indicator" :class="getStepColor(step)">
                {{ getStepLabel(step) }}
              </span>
              <span class="step-text">{{ step.content }}</span>
            </div>
          </div>
        </div>

        <!-- 加载中（pending 且没有内容时显示） -->
        <div
          v-if="isPending && !message.content && !message.voice"
          class="chat-loading"
        >
          <span class="loading-dot"></span>
          <span class="loading-dot"></span>
          <span class="loading-dot"></span>
        </div>

        <!-- 正常内容（有内容时显示，包括流式输出中） -->
        <template v-else>
          <!-- 语音消息 -->
          <div
            v-if="message.voice"
            class="voice-message"
            @click="toggleVoicePlay"
          >
            <div class="voice-waves">
              <span
                v-for="i in 8"
                :key="i"
                class="wave-bar"
                :class="{ playing: isPlaying }"
              ></span>
            </div>
            <!-- <span class="voice-duration">{{ formatVoiceDuration(message.voice.duration) }}</span> -->
          </div>

          <!-- 语音识别状态 -->
          <div v-if="message.voice" class="voice-transcribe-status">
            <template v-if="message.voice.transcribeStatus === 'transcribing'">
              <Loader2 class="size-3 animate-spin" />
              <span>正在识别...</span>
            </template>
            <template v-else-if="message.voice.transcribedText">
              <span class="transcribed-text">{{
                message.voice.transcribedText
              }}</span>
            </template>
          </div>

          <!-- 附件预览 -->
          <div v-if="message.attachments?.length" class="chat-attachments">
            <div
              v-for="att in message.attachments"
              :key="att.id"
              class="attachment-item"
              @click="$emit('preview', att)"
            >
              <img
                v-if="att.type === 'image' && (att.thumbnail || att.url)"
                :src="att.thumbnail || att.url"
                class="attachment-image"
              />
              <div v-else class="attachment-file">
                <component :is="getAttachmentIcon(att.type)" class="size-5" />
                <div class="file-info">
                  <div class="file-name">{{ att.name }}</div>
                  <div v-if="att.size > 0" class="file-size">
                    {{ formatFileSize(att.size) }}
                  </div>
                </div>
              </div>
            </div>
          </div>

          <!-- 文本内容（语音消息不显示重复的文本） -->
          <div v-if="isUser && !message.voice" class="chat-text">
            {{ message.content }}
          </div>
          <div
            v-else-if="!isUser"
            class="chat-text markdown-body"
            v-html="renderedContent"
          ></div>

          <!-- 错误信息 -->
          <div v-if="isFailed" class="chat-error">
            <AlertCircle class="size-4" />
            <span>{{ message.error_message || '发送失败' }}</span>
          </div>

          <!-- 对话流交互区域 -->
          <div v-if="message.interaction" class="chat-interaction">
            <!-- 问答输入 -->
            <template
              v-if="
                message.interaction.type === 'question' &&
                message.interaction.render_input
              "
            >
              <input
                v-model="interactionInputValue"
                :type="
                  message.interaction.input_type === 'number'
                    ? 'number'
                    : 'text'
                "
                :placeholder="message.interaction.placeholder || '请输入...'"
                class="interaction-input"
                @keyup.enter="
                  $emit('interaction-submit', message.id, interactionInputValue)
                "
              />
              <div class="interaction-buttons">
                <button
                  class="interaction-btn primary"
                  @click="
                    $emit(
                      'interaction-submit',
                      message.id,
                      interactionInputValue,
                    )
                  "
                >
                  提交
                </button>
              </div>
            </template>

            <!-- 选项选择 -->
            <template v-else-if="message.interaction.type === 'choice'">
              <div class="interaction-options">
                <button
                  v-for="opt in message.interaction.options"
                  :key="opt.value"
                  class="option-btn"
                  :class="{
                    selected: message.interaction.multiple
                      ? (interactionChoiceValue as string[]).includes(opt.value)
                      : interactionChoiceValue === opt.value,
                  }"
                  @click="
                    message.interaction?.multiple
                      ? toggleMultiChoice(opt.value)
                      : (interactionChoiceValue = opt.value)
                  "
                >
                  {{ opt.label }}
                </button>
              </div>
              <div class="interaction-buttons">
                <button
                  class="interaction-btn primary"
                  @click="
                    $emit(
                      'interaction-submit',
                      message.id,
                      interactionChoiceValue,
                    )
                  "
                >
                  确定
                </button>
              </div>
            </template>

            <!-- 确认操作 -->
            <template v-else-if="message.interaction.type === 'confirm'">
              <div class="interaction-buttons confirm-buttons">
                <button
                  class="interaction-btn"
                  @click="$emit('interaction-cancel', message.id)"
                >
                  {{ message.interaction.cancel_text || '取消' }}
                </button>
                <button
                  class="interaction-btn primary"
                  @click="$emit('interaction-submit', message.id, true)"
                >
                  {{ message.interaction.confirm_text || '确认' }}
                </button>
              </div>
            </template>
          </div>
        </template>
      </div>

      <!-- 消息操作栏 -->
      <div
        v-if="
          !isUser &&
          (isCompleted || message.status === 'streaming') &&
          message.content
        "
        class="chat-actions"
      >
        <div class="action-info">
          <span v-if="message.elapsed_time">{{ message.elapsed_time }}ms</span>
          <span v-if="message.tokens_used">{{ message.tokens_used }} tokens</span>
        </div>
        <div class="action-buttons">
          <button class="action-btn" title="复制" @click="handleCopy">
            <Copy class="size-4" />
          </button>
          <button
            v-if="enableRegenerate && isLast"
            class="action-btn"
            title="重新生成"
            @click="$emit('regenerate', message.id)"
          >
            <RefreshCw class="size-4" />
          </button>
          <template v-if="enableFeedback">
            <button
              class="action-btn"
              :class="{ active: message.feedback === 'like' }"
              title="有帮助"
              @click="$emit('feedback', message.id, 'like')"
            >
              <ThumbsUp class="size-4" />
            </button>
            <button
              class="action-btn is-dislike"
              :class="{ active: message.feedback === 'dislike' }"
              title="没帮助"
              @click="$emit('feedback', message.id, 'dislike')"
            >
              <ThumbsDown class="size-4" />
            </button>
          </template>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.chat-message {
  display: flex;
  gap: 12px;
  padding: 16px 0;
}

.chat-message.is-user {
  flex-direction: row-reverse;
}

.chat-avatar {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--el-fill-color-light);
  color: var(--el-text-color-secondary);
  overflow: hidden;
}

.chat-message.is-user .chat-avatar {
  background: var(--el-color-primary);
  color: #fff;
}

.chat-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.chat-content {
  flex: 1;
  min-width: 0;
  max-width: 80%;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.chat-message.is-user .chat-content {
  align-items: flex-end;
}

.chat-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--el-text-color-secondary);
  margin-bottom: 2px;
}

/* 推理步骤 */
.chat-reasoning {
  width: 100%;
  margin-bottom: 8px;
  border-radius: 12px;
  border: 1px solid var(--el-border-color-lighter);
  background: var(--el-fill-color-lighter);
  overflow: hidden;
}

.reasoning-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 12px;
  font-size: 13px;
  color: var(--el-text-color-secondary);
  cursor: pointer;
  transition: background 0.2s;
}

.reasoning-header:hover {
  background: var(--el-fill-color-light);
}

.reasoning-steps {
  border-top: 1px solid var(--el-border-color-lighter);
  padding: 12px;
}

.reasoning-step {
  padding: 8px 0 8px 12px;
  border-left: 2px solid var(--el-border-color);
}

.reasoning-step + .reasoning-step {
  margin-top: 8px;
}

.step-header {
  display: flex;
  align-items: center;
  gap: 8px;
}

.step-type {
  font-size: 12px;
  font-weight: 500;
}

.step-tool {
  font-size: 12px;
  color: var(--el-text-color-placeholder);
}

.step-content {
  margin-top: 4px;
  font-size: 13px;
  color: var(--el-text-color-regular);
  white-space: pre-wrap;
  line-height: 1.5;
}

.step-params {
  margin-top: 6px;
  padding: 8px;
  border-radius: 6px;
  background: var(--el-fill-color);
  font-size: 12px;
  overflow-x: auto;
}

/* 消息气泡 */
.chat-bubble {
  padding: 12px 16px;
  border-radius: 18px;
  background: var(--el-fill-color-light);
  color: var(--el-text-color-primary);
  word-break: break-word;
}

.chat-message.is-user .chat-bubble {
  background: var(--el-color-primary);
  color: #fff;
  border-bottom-right-radius: 4px;
}

.chat-message:not(.is-user) .chat-bubble {
  border-bottom-left-radius: 4px;
}

.chat-bubble.is-error {
  border: 1px solid var(--el-color-danger-light-5);
}

/* 加载动画 */
.chat-loading {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 0;
}

.loading-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--el-text-color-placeholder);
  animation: loading-bounce 1.4s infinite ease-in-out both;
}

.loading-dot:nth-child(1) {
  animation-delay: -0.32s;
}
.loading-dot:nth-child(2) {
  animation-delay: -0.16s;
}

@keyframes loading-bounce {
  0%,
  80%,
  100% {
    transform: scale(0.6);
    opacity: 0.5;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}

/* 附件 */
.chat-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 8px;
}

.attachment-item {
  cursor: pointer;
  border-radius: 8px;
  overflow: hidden;
}

.attachment-image {
  max-width: 200px;
  max-height: 150px;
  object-fit: cover;
  border-radius: 8px;
}

.attachment-file {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: rgba(0, 0, 0, 0.05);
  border-radius: 8px;
}

.chat-message.is-user .attachment-file {
  background: rgba(255, 255, 255, 0.15);
}

.file-info {
  min-width: 0;
}

.file-name {
  font-size: 13px;
  max-width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-size {
  font-size: 11px;
  opacity: 0.7;
}

/* 文本内容 */
.chat-text {
  font-size: 14px;
  line-height: 1.6;
  white-space: pre-wrap;
}

/* Markdown 样式 */
.chat-text.markdown-body {
  white-space: normal;
}

.chat-text.markdown-body :deep(p) {
  margin: 0 0 0.75em;
}

.chat-text.markdown-body :deep(p:last-child) {
  margin-bottom: 0;
}

.chat-text.markdown-body :deep(h1),
.chat-text.markdown-body :deep(h2),
.chat-text.markdown-body :deep(h3),
.chat-text.markdown-body :deep(h4),
.chat-text.markdown-body :deep(h5),
.chat-text.markdown-body :deep(h6) {
  margin: 1em 0 0.5em;
  font-weight: 600;
  line-height: 1.4;
}

.chat-text.markdown-body :deep(h1) {
  font-size: 1.5em;
}
.chat-text.markdown-body :deep(h2) {
  font-size: 1.3em;
}
.chat-text.markdown-body :deep(h3) {
  font-size: 1.15em;
}
.chat-text.markdown-body :deep(h4) {
  font-size: 1em;
}

.chat-text.markdown-body :deep(h1:first-child),
.chat-text.markdown-body :deep(h2:first-child),
.chat-text.markdown-body :deep(h3:first-child) {
  margin-top: 0;
}

.chat-text.markdown-body :deep(ul),
.chat-text.markdown-body :deep(ol) {
  margin: 0.5em 0;
  padding-left: 1.5em;
}

.chat-text.markdown-body :deep(li) {
  margin: 0.25em 0;
}

.chat-text.markdown-body :deep(code) {
  padding: 0.15em 0.4em;
  border-radius: 4px;
  background: var(--el-fill-color);
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 0.9em;
}

.chat-text.markdown-body :deep(pre) {
  margin: 0.75em 0;
  padding: 12px 16px;
  border-radius: 8px;
  background: var(--el-fill-color-dark);
  overflow-x: auto;
}

.chat-text.markdown-body :deep(pre code) {
  padding: 0;
  background: transparent;
  font-size: 13px;
  line-height: 1.5;
}

.chat-text.markdown-body :deep(blockquote) {
  margin: 0.75em 0;
  padding: 0.5em 1em;
  border-left: 3px solid var(--el-color-primary);
  background: var(--el-fill-color-lighter);
  color: var(--el-text-color-secondary);
}

.chat-text.markdown-body :deep(blockquote p) {
  margin: 0;
}

.chat-text.markdown-body :deep(a) {
  color: var(--el-color-primary);
  text-decoration: none;
}

.chat-text.markdown-body :deep(a:hover) {
  text-decoration: underline;
}

.chat-text.markdown-body :deep(table) {
  width: 100%;
  margin: 0.75em 0;
  border-collapse: collapse;
  font-size: 13px;
}

.chat-text.markdown-body :deep(th),
.chat-text.markdown-body :deep(td) {
  padding: 8px 12px;
  border: 1px solid var(--el-border-color);
  text-align: left;
}

.chat-text.markdown-body :deep(th) {
  background: var(--el-fill-color-light);
  font-weight: 600;
}

.chat-text.markdown-body :deep(tr:nth-child(even)) {
  background: var(--el-fill-color-lighter);
}

.chat-text.markdown-body :deep(hr) {
  margin: 1em 0;
  border: none;
  border-top: 1px solid var(--el-border-color);
}

.chat-text.markdown-body :deep(img) {
  max-width: 100%;
  border-radius: 8px;
}

.chat-text.markdown-body :deep(strong) {
  font-weight: 600;
}

.chat-text.markdown-body :deep(em) {
  font-style: italic;
}

/* 错误信息 */
.chat-error {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 8px;
  font-size: 13px;
  color: var(--el-color-danger);
}

/* 操作栏 */
.chat-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 4px;
  padding: 0 4px;
}

.action-info {
  display: flex;
  gap: 12px;
  font-size: 12px;
  color: var(--el-text-color-placeholder);
}

.action-buttons {
  display: flex;
  gap: 2px;
  opacity: 0;
  transition: opacity 0.2s;
}

.chat-message:hover .action-buttons {
  opacity: 1;
}

.action-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--el-text-color-secondary);
  cursor: pointer;
  transition: all 0.2s;
}

.action-btn:hover {
  background: var(--el-fill-color);
  color: var(--el-text-color-primary);
}

.action-btn.active {
  color: var(--el-color-primary);
}

.action-btn.active.is-dislike {
  color: var(--el-color-danger);
}

/* 语音消息 */
.voice-message {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  cursor: pointer;
  min-width: 120px;
}

.voice-play-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.1);
  flex-shrink: 0;
}

.chat-message.is-user .voice-play-btn {
  background: rgba(255, 255, 255, 0.2);
}

.voice-waves {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  flex: 1;
}

.wave-bar {
  width: 3px;
  border-radius: 2px;
  background: currentColor;
  opacity: 0.5;
}

.wave-bar:nth-child(1) {
  height: 10px;
}
.wave-bar:nth-child(2) {
  height: 16px;
}
.wave-bar:nth-child(3) {
  height: 12px;
}
.wave-bar:nth-child(4) {
  height: 18px;
}
.wave-bar:nth-child(5) {
  height: 10px;
}
.wave-bar:nth-child(6) {
  height: 16px;
}
.wave-bar:nth-child(7) {
  height: 12px;
}
.wave-bar:nth-child(8) {
  height: 18px;
}

.wave-bar.playing {
  animation: wave-animation 0.5s ease-in-out infinite alternate;
}

.wave-bar.playing:nth-child(1) {
  animation-delay: 0s;
}
.wave-bar.playing:nth-child(2) {
  animation-delay: 0.1s;
}
.wave-bar.playing:nth-child(3) {
  animation-delay: 0.2s;
}
.wave-bar.playing:nth-child(4) {
  animation-delay: 0.3s;
}
.wave-bar.playing:nth-child(5) {
  animation-delay: 0.4s;
}
.wave-bar.playing:nth-child(6) {
  animation-delay: 0.5s;
}
.wave-bar.playing:nth-child(7) {
  animation-delay: 0.6s;
}
.wave-bar.playing:nth-child(8) {
  animation-delay: 0.7s;
}

@keyframes wave-animation {
  0% {
    transform: scaleY(0.5);
    opacity: 0.4;
  }
  100% {
    transform: scaleY(1.2);
    opacity: 1;
  }
}

.voice-duration {
  font-size: 12px;
  opacity: 0.8;
  flex-shrink: 0;
}

/* 语音识别状态 */
.voice-transcribe-status {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid rgba(0, 0, 0, 0.1);
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.chat-message.is-user .voice-transcribe-status {
  border-top-color: rgba(255, 255, 255, 0.2);
  color: rgba(255, 255, 255, 0.8);
}

.transcribed-text {
  font-size: 13px;
}

/* 对话流交互区域 */
.chat-interaction {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--el-border-color-lighter);
}

.interaction-input {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--el-border-color);
  border-radius: 8px;
  font-size: 14px;
  outline: none;
  transition: border-color 0.2s;
  background: var(--el-bg-color);
  color: var(--el-text-color-primary);
}

.interaction-input:focus {
  border-color: var(--el-color-primary);
}

.interaction-options {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}

.option-btn {
  padding: 8px 16px;
  border: 1px solid var(--el-border-color);
  border-radius: 20px;
  background: var(--el-bg-color);
  color: var(--el-text-color-regular);
  font-size: 13px;
  cursor: pointer;
  transition: all 0.2s;
}

.option-btn:hover {
  border-color: var(--el-color-primary);
  color: var(--el-color-primary);
}

.option-btn.selected {
  border-color: var(--el-color-primary);
  background: var(--el-color-primary-light-9);
  color: var(--el-color-primary);
}

.interaction-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 12px;
}

.interaction-buttons.confirm-buttons {
  justify-content: center;
}

.interaction-btn {
  padding: 8px 20px;
  border: 1px solid var(--el-border-color);
  border-radius: 8px;
  background: var(--el-bg-color);
  color: var(--el-text-color-regular);
  font-size: 14px;
  cursor: pointer;
  transition: all 0.2s;
}

.interaction-btn:hover {
  border-color: var(--el-color-primary);
  color: var(--el-color-primary);
}

.interaction-btn.primary {
  border-color: var(--el-color-primary);
  background: var(--el-color-primary);
  color: #fff;
}

.interaction-btn.primary:hover {
  background: var(--el-color-primary-dark-2);
  border-color: var(--el-color-primary-dark-2);
}

/* 气泡内的执行步骤 */
.bubble-steps {
  margin-bottom: 8px;
  padding-bottom: 8px;
  border-bottom: 1px dashed var(--el-border-color-lighter);
}

.bubble-steps-header {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 0;
  font-size: 12px;
  color: var(--el-text-color-secondary);
  cursor: pointer;
  user-select: none;
}

.bubble-steps-header:hover {
  color: var(--el-text-color-primary);
}

.steps-status {
  font-weight: 500;
}

.steps-status.running {
  color: var(--el-color-primary);
}

.bubble-steps-list {
  margin-top: 4px;
  padding-left: 4px;
}

.bubble-step {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 0;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.bubble-step.is-last {
  color: var(--el-text-color-primary);
  font-weight: 500;
}

.step-indicator {
  flex-shrink: 0;
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 500;
  background: var(--el-fill-color-light);
}

.step-text {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>
