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

/**
 * 输入区域组件 - ChatGPT 风格
 * 支持文本、附件、图片、语音输入（带语音识别）
 */
import { computed, ref } from 'vue';

import { Loader2, Mic, MicOff, Paperclip, Send, Square, X } from '@vben/icons';

import { ElMessage, ElUpload } from 'element-plus';

import { transcribeAudioApi } from '#/api/ai-platform/ai-platform';
import { uploadFile } from '#/api/core/file';

const props = withDefaults(
  defineProps<{
    acceptFileTypes?: string;
    disabled?: boolean;
    enableAttachment?: boolean;
    enableImage?: boolean;
    enableVoice?: boolean;
    loading?: boolean;
    maxAttachmentSize?: number;
    placeholder?: string;
    /** 语音模式：'transcribe' 识别后填入输入框，'send' 识别后直接发送（类似微信语音） */
    voiceMode?: 'send' | 'transcribe';
  }>(),
  {
    loading: false,
    disabled: false,
    placeholder: '输入消息...',
    enableAttachment: true,
    enableImage: true,
    enableVoice: true,
    voiceMode: 'send', // 默认使用微信语音模式
    maxAttachmentSize: 10,
    acceptFileTypes: '.pdf,.doc,.docx,.txt,.md,.json,.csv,.xlsx,.ppt,.pptx',
  },
);

const emit = defineEmits<{
  (e: 'send', message: string, attachments?: Attachment[]): void;
  (e: 'stop'): void;
  (e: 'upload', file: File): Promise<Attachment>;
  /** 发送语音消息（send 模式） */
  (e: 'send-voice', voice: VoiceMessage, audioBlob: Blob): void;
  /** 语音识别完成（send 模式） */
  (e: 'voice-transcribed', voiceId: string, text: string): void;
}>();

// 输入内容
const inputText = ref('');
const textareaRef = ref<HTMLTextAreaElement | null>(null);

// 附件列表
const attachments = ref<Attachment[]>([]);
const uploading = ref(false);

// 语音录制状态
const isRecording = ref(false);
const isTranscribing = ref(false); // 语音识别中
const recordingTime = ref(0);
let recordingTimer: null | ReturnType<typeof setInterval> = null;
let mediaStream: MediaStream | null = null;

// 拖拽状态
const isDragging = ref(false);
let dragCounter = 0;

// 处理拖拽进入
const handleDragEnter = (e: DragEvent) => {
  e.preventDefault();
  e.stopPropagation();
  dragCounter++;
  if (e.dataTransfer?.types.includes('Files')) {
    isDragging.value = true;
  }
};

// 处理拖拽离开
const handleDragLeave = (e: DragEvent) => {
  e.preventDefault();
  e.stopPropagation();
  dragCounter--;
  if (dragCounter === 0) {
    isDragging.value = false;
  }
};

// 处理拖拽悬停
const handleDragOver = (e: DragEvent) => {
  e.preventDefault();
  e.stopPropagation();
};

// 处理拖拽放下
const handleDrop = async (e: DragEvent) => {
  e.preventDefault();
  e.stopPropagation();
  isDragging.value = false;
  dragCounter = 0;

  const files = e.dataTransfer?.files;
  if (files && files.length > 0) {
    for (const file of files) {
      await handleFileChange(file);
    }
  }
};

// 是否有文件正在上传
const hasUploadingFiles = computed(() => {
  return attachments.value.some((att) => att.status === 'uploading');
});

// 是否可以发送（必须输入文字才能发送）
const canSend = computed(() => {
  const hasText = inputText.value.trim().length > 0;
  return (
    hasText && !props.loading && !props.disabled && !hasUploadingFiles.value
  );
});

// 自动调整高度
const adjustHeight = () => {
  if (textareaRef.value) {
    textareaRef.value.style.height = 'auto';
    textareaRef.value.style.height = `${Math.min(textareaRef.value.scrollHeight, 200)}px`;
  }
};

// 处理键盘事件
const handleKeydown = (e: KeyboardEvent) => {
  if (e.key === 'Enter' && !e.shiftKey) {
    e.preventDefault();
    handleSend();
  }
};

// 处理粘贴事件（支持粘贴文件和图片）
const handlePaste = async (e: ClipboardEvent) => {
  const items = e.clipboardData?.items;
  if (!items) return;

  for (const item of items) {
    // 处理图片
    if (item.type.startsWith('image/')) {
      e.preventDefault();
      const file = item.getAsFile();
      if (file) {
        await handleFileChange(file);
      }
    }
    // 处理文件
    else if (item.kind === 'file') {
      e.preventDefault();
      const file = item.getAsFile();
      if (file) {
        await handleFileChange(file);
      }
    }
  }
};

// 发送消息
const handleSend = () => {
  if (!canSend.value) return;

  const message = inputText.value.trim();
  // 只发送上传成功的附件（过滤掉上传中和失败的）
  const successFiles = attachments.value.filter(
    (att) =>
      att.status === 'success' ||
      (!att.status && att.file_id && !att.file_id.startsWith('temp-')),
  );

  emit('send', message, successFiles.length > 0 ? successFiles : undefined);

  // 清空
  inputText.value = '';
  attachments.value = [];
  if (textareaRef.value) {
    textareaRef.value.style.height = 'auto';
  }
};

// 停止生成
const handleStop = () => {
  emit('stop');
};

// 处理文件上传 - 上传到文件管理系统
const handleFileChange = async (file: File) => {
  // 检查文件大小
  if (file.size > props.maxAttachmentSize * 1024 * 1024) {
    ElMessage.warning(`文件大小不能超过 ${props.maxAttachmentSize}MB`);
    return false;
  }

  uploading.value = true;

  const fileType: AttachmentType = file.type.startsWith('image/')
    ? 'image'
    : file.type.startsWith('audio/')
      ? 'audio'
      : file.type.startsWith('video/')
        ? 'video'
        : 'file';
  const localPreviewUrl = URL.createObjectURL(file);
  const tempId = `temp-${Date.now()}`;

  // 先添加一个上传中的占位附件
  const attachment: Attachment = {
    id: tempId,
    file_id: tempId,
    type: fileType,
    name: file.name,
    url: localPreviewUrl,
    size: file.size,
    status: 'uploading',
    progress: 0,
  };

  if (fileType === 'image') {
    attachment.thumbnail = localPreviewUrl;
  }

  attachments.value.push(attachment);

  try {
    // 上传到文件管理系统
    const response = await uploadFile(file, {
      isPublic: true, // 公开文件，方便 AI 访问
      source: 'chat',
      onProgress: (progressEvent) => {
        const item = attachments.value.find((a) => a.id === tempId);
        if (item) {
          item.progress = progressEvent.percentage;
        }
      },
    });

    // 更新为真实的文件信息
    const item = attachments.value.find((a) => a.id === tempId);
    if (item && response) {
      item.id = String(response.id);
      item.file_id = String(response.id);
      item.url = localPreviewUrl; // 继续使用本地预览
      item.status = 'success';
      item.progress = 100;
    }
  } catch (error) {
    console.error('文件上传失败:', error);
    ElMessage.error('文件上传失败');

    // 标记为失败状态
    const item = attachments.value.find((a) => a.id === tempId);
    if (item) {
      item.status = 'error';
      item.progress = 0;
    }
  } finally {
    uploading.value = false;
  }

  return false; // 阻止默认上传
};

// 移除附件
const removeAttachment = (index: number) => {
  const attachment = attachments.value[index];
  if (attachment?.url?.startsWith('blob:')) {
    URL.revokeObjectURL(attachment.url);
  }
  attachments.value.splice(index, 1);
};

// AudioContext 用于录制 PCM 格式
let audioContext: AudioContext | null = null;
let scriptProcessor: null | ScriptProcessorNode = null;
let pcmData: Int16Array[] = [];

// 开始录音
const startRecording = async () => {
  try {
    mediaStream = await navigator.mediaDevices.getUserMedia({
      audio: {
        sampleRate: 16_000,
        channelCount: 1,
        echoCancellation: true,
        noiseSuppression: true,
      },
    });

    // 使用 AudioContext 录制 PCM 格式（阿里云直接支持）
    audioContext = new AudioContext({ sampleRate: 16_000 });
    const source = audioContext.createMediaStreamSource(mediaStream);

    // 创建处理器节点
    scriptProcessor = audioContext.createScriptProcessor(4096, 1, 1);
    pcmData = [];

    scriptProcessor.onaudioprocess = (e) => {
      const inputData = e.inputBuffer.getChannelData(0);
      // 转换为 16 位 PCM
      const pcm16 = new Int16Array(inputData.length);
      for (const [i, inputDatum] of inputData.entries()) {
        const s = Math.max(-1, Math.min(1, inputDatum!));
        pcm16[i] = s < 0 ? s * 0x80_00 : s * 0x7f_ff;
      }
      pcmData.push(pcm16);
    };

    source.connect(scriptProcessor);
    scriptProcessor.connect(audioContext.destination);

    isRecording.value = true;
    recordingTime.value = 0;

    // 计时
    recordingTimer = setInterval(() => {
      recordingTime.value++;
    }, 1000);
  } catch (error) {
    console.error('录音失败:', error);
    ElMessage.error('无法访问麦克风');
  }
};

// 停止录音
const stopRecording = async () => {
  if (!isRecording.value) return;

  isRecording.value = false;
  if (recordingTimer) {
    clearInterval(recordingTimer);
    recordingTimer = null;
  }

  // 停止音频处理
  scriptProcessor?.disconnect();
  scriptProcessor = null;

  // 停止媒体流
  mediaStream?.getTracks().forEach((track) => track.stop());
  mediaStream = null;

  // 合并 PCM 数据
  const totalLength = pcmData.reduce((acc, arr) => acc + arr.length, 0);
  const mergedPcm = new Int16Array(totalLength);
  let offset = 0;
  for (const chunk of pcmData) {
    mergedPcm.set(chunk, offset);
    offset += chunk.length;
  }
  pcmData = [];

  // 创建 WAV 文件（带头部）
  const wavBlob = createWavBlob(mergedPcm, 16_000);

  // 关闭 AudioContext
  await audioContext?.close();
  audioContext = null;

  if (props.voiceMode === 'send') {
    // 微信语音模式：先发送语音消息到聊天框，再进行识别
    const audioUrl = URL.createObjectURL(wavBlob);
    const voiceId = `voice-${Date.now()}`;
    const duration = recordingTime.value;

    // 发送语音消息事件（让父组件显示语音消息，传递 voiceId 以便后续更新状态）
    emit(
      'send-voice',
      {
        audioUrl,
        duration,
        transcribeStatus: 'transcribing',
        voiceId, // 传递 ID 以便后续更新
      } as any,
      wavBlob,
    );

    // 后台进行语音识别
    isTranscribing.value = true;
    try {
      const result = await transcribeAudioApi(wavBlob, { language: 'zh' });
      if (result.text) {
        // 通知父组件识别完成（文字显示在语音消息下方，同时触发模型回复）
        emit('voice-transcribed', voiceId, result.text);
      } else {
        emit('voice-transcribed', voiceId, '[未识别到语音内容]');
      }
    } catch (error: any) {
      console.error('语音识别失败:', error);
      emit(
        'voice-transcribed',
        voiceId,
        `[识别失败: ${error?.message || '未知错误'}]`,
      );
    } finally {
      isTranscribing.value = false;
    }
  } else {
    // 转写模式：识别后填入输入框
    isTranscribing.value = true;
    try {
      const result = await transcribeAudioApi(wavBlob, { language: 'zh' });
      if (result.text) {
        inputText.value =
          (inputText.value ? `${inputText.value} ` : '') + result.text;
        adjustHeight();
        ElMessage.success('语音识别完成');
      } else {
        ElMessage.warning('未识别到语音内容');
      }
    } catch (error: any) {
      console.error('语音识别失败:', error);
      ElMessage.error(error?.message || '语音识别失败');

      // 识别失败时，将录音作为附件保存
      const audioUrl = URL.createObjectURL(wavBlob);
      attachments.value.push({
        id: `audio-${Date.now()}`,
        type: 'audio',
        name: `录音_${new Date().toLocaleTimeString()}.wav`,
        url: audioUrl,
        size: wavBlob.size,
        duration: recordingTime.value,
      });
    } finally {
      isTranscribing.value = false;
    }
  }
};

// 创建 WAV 文件
function createWavBlob(pcmData: Int16Array, sampleRate: number): Blob {
  const buffer = new ArrayBuffer(44 + pcmData.length * 2);
  const view = new DataView(buffer);

  // WAV 文件头
  const writeString = (offset: number, str: string) => {
    for (let i = 0; i < str.length; i++) {
      view.setUint8(offset + i, str.charCodeAt(i));
    }
  };

  writeString(0, 'RIFF');
  view.setUint32(4, 36 + pcmData.length * 2, true);
  writeString(8, 'WAVE');
  writeString(12, 'fmt ');
  view.setUint32(16, 16, true); // fmt chunk size
  view.setUint16(20, 1, true); // PCM format
  view.setUint16(22, 1, true); // mono
  view.setUint32(24, sampleRate, true);
  view.setUint32(28, sampleRate * 2, true); // byte rate
  view.setUint16(32, 2, true); // block align
  view.setUint16(34, 16, true); // bits per sample
  writeString(36, 'data');
  view.setUint32(40, pcmData.length * 2, true);

  // 写入 PCM 数据
  const pcmOffset = 44;
  for (const [i, pcmDatum] of pcmData.entries()) {
    view.setInt16(pcmOffset + i * 2, pcmDatum!, true);
  }

  return new Blob([buffer], { type: 'audio/wav' });
}

// 格式化录音时间
const formatRecordingTime = (seconds: number) => {
  const mins = Math.floor(seconds / 60);
  const secs = seconds % 60;
  return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
};
</script>

<template>
  <div
    class="input-area-wrapper"
    :class="{ 'is-dragging': isDragging }"
    @dragenter="handleDragEnter"
    @dragleave="handleDragLeave"
    @dragover="handleDragOver"
    @drop="handleDrop"
  >
    <!-- 拖拽提示遮罩 -->
    <div v-if="isDragging" class="drag-overlay">
      <div class="drag-content">
        <Paperclip class="size-8 text-[var(--el-color-primary)]" />
        <span>将文件拖到此处上传</span>
      </div>
    </div>
    <!-- 附件预览（在输入框上方显示） -->
    <div v-if="attachments.length > 0" class="attachments-preview">
      <div
        v-for="(attachment, index) in attachments"
        :key="attachment.id"
        class="attachment-item"
      >
        <!-- 图片预览 -->
        <div v-if="attachment.type === 'image'" class="attachment-image">
          <img
            :src="attachment.thumbnail || attachment.url"
            :alt="attachment.name"
          />
          <!-- 上传进度遮罩 -->
          <div v-if="attachment.status === 'uploading'" class="upload-overlay">
            <Loader2 class="size-4 animate-spin text-white" />
            <span class="upload-progress">{{ attachment.progress || 0 }}%</span>
          </div>
          <!-- 上传失败遮罩 -->
          <div
            v-else-if="attachment.status === 'error'"
            class="upload-overlay error"
          >
            <X class="size-4 text-white" />
          </div>
          <button class="remove-btn" @click="removeAttachment(index)">
            <X class="size-3" />
          </button>
        </div>

        <!-- 文件预览 -->
        <div
          v-else-if="attachment.type === 'file'"
          class="attachment-file"
          :class="{
            'is-uploading': attachment.status === 'uploading',
            'is-error': attachment.status === 'error',
          }"
        >
          <Loader2
            v-if="attachment.status === 'uploading'"
            class="size-4 animate-spin"
          />
          <Paperclip v-else class="size-4" />
          <span class="file-name">{{ attachment.name }}</span>
          <span v-if="attachment.status === 'uploading'" class="upload-percent">{{ attachment.progress || 0 }}%</span>
          <button class="remove-btn" @click="removeAttachment(index)">
            <X class="size-3" />
          </button>
        </div>

        <!-- 音频/视频预览 -->
        <div
          v-else
          class="attachment-file"
          :class="{
            'is-uploading': attachment.status === 'uploading',
            'is-error': attachment.status === 'error',
          }"
        >
          <Loader2
            v-if="attachment.status === 'uploading'"
            class="size-4 animate-spin"
          />
          <Mic v-else class="size-4" />
          <span class="file-name">{{
            attachment.name ||
            (attachment.duration ? `${attachment.duration}s` : '')
          }}</span>
          <span v-if="attachment.status === 'uploading'" class="upload-percent">{{ attachment.progress || 0 }}%</span>
          <button class="remove-btn" @click="removeAttachment(index)">
            <X class="size-3" />
          </button>
        </div>
      </div>
    </div>

    <!-- 录音状态 -->
    <div v-if="isRecording" class="recording-bar">
      <span class="recording-dot"></span>
      <span class="recording-text">正在录音{{ voiceMode === 'send' ? '，松开发送' : '' }}</span>
      <span class="recording-time">{{
        formatRecordingTime(recordingTime)
      }}</span>
      <button class="stop-btn" @click="stopRecording">
        <Send v-if="voiceMode === 'send'" class="size-3" />
        <Square v-else class="size-3" />
        <span>{{ voiceMode === 'send' ? '发送' : '停止' }}</span>
      </button>
    </div>

    <!-- 语音识别中 -->
    <div v-if="isTranscribing" class="transcribing-bar">
      <Loader2 class="size-4 animate-spin" />
      <span class="transcribing-text">{{
        voiceMode === 'send' ? '识别并发送中...' : '语音识别中...'
      }}</span>
    </div>

    <!-- 主输入容器 -->
    <div class="input-container">
      <!-- 输入框 -->
      <textarea
        ref="textareaRef"
        v-model="inputText"
        :placeholder="placeholder"
        :disabled="disabled || isRecording"
        rows="2"
        class="input-textarea"
        @input="adjustHeight"
        @keydown="handleKeydown"
        @paste="handlePaste"
      ></textarea>

      <!-- 底部工具栏 -->
      <div class="input-toolbar">
        <!-- 左侧：附件按钮 -->
        <div class="toolbar-left">
          <ElUpload
            v-if="enableAttachment || enableImage"
            :show-file-list="false"
            :accept="
              enableImage ? `image/*,${acceptFileTypes}` : acceptFileTypes
            "
            :before-upload="handleFileChange"
          >
            <button
              class="toolbar-btn"
              :disabled="disabled || uploading"
              title="上传附件"
            >
              <Paperclip class="size-5" />
            </button>
          </ElUpload>

          <!-- 上传中指示器 -->
          <Loader2 v-if="uploading" class="size-4 animate-spin text-gray-400" />
        </div>

        <!-- 右侧：语音 + 发送 -->
        <div class="toolbar-right">
          <!-- 语音按钮 -->
          <button
            v-if="enableVoice"
            class="toolbar-btn"
            :class="{ 'is-recording': isRecording }"
            :disabled="disabled"
            :title="isRecording ? '停止录音' : '语音输入'"
            @click="isRecording ? stopRecording() : startRecording()"
          >
            <component :is="isRecording ? MicOff : Mic" class="size-5" />
          </button>

          <!-- 发送/停止按钮 -->
          <button
            v-if="loading"
            class="send-btn is-stop"
            title="停止生成"
            @click="handleStop"
          >
            <Square class="size-4" />
          </button>
          <button
            v-else
            class="send-btn"
            :class="{ 'can-send': canSend }"
            :disabled="!canSend"
            title="发送"
            @click="handleSend"
          >
            <Send class="size-4" />
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
/* 整体包装器 */
.input-area-wrapper {
  position: relative;
  padding: 12px 16px;
  background: var(--el-bg-color);
}

.input-area-wrapper.is-dragging {
  background: var(--el-color-primary-light-9);
}

/* 拖拽提示遮罩 */
.drag-overlay {
  position: absolute;
  inset: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--el-color-primary-light-9);
  border: 2px dashed var(--el-color-primary);
  border-radius: 12px;
  margin: 8px;
}

.drag-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: var(--el-color-primary);
  font-size: 14px;
  font-weight: 500;
}

/* 附件预览区 */
.attachments-preview {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}

.attachment-item {
  position: relative;
}

.attachment-image {
  position: relative;
  width: 64px;
  height: 64px;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--el-border-color-lighter);
}

.attachment-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.attachment-file {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border-radius: 8px;
  background: var(--el-fill-color-light);
  color: var(--el-text-color-secondary);
  font-size: 12px;
}

.file-name {
  max-width: 100px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.attachment-item .remove-btn {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 20px;
  height: 20px;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: all 0.2s;
  z-index: 1;
}

.attachment-item:hover .remove-btn {
  opacity: 1;
}

.attachment-item .remove-btn:hover {
  background: rgba(0, 0, 0, 0.8);
  transform: scale(1.1);
}

/* 上传进度遮罩 */
.upload-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 8px;
}

.upload-overlay.error {
  background: rgba(239, 68, 68, 0.7);
}

.upload-progress {
  font-size: 10px;
  color: white;
  font-weight: 500;
}

/* 文件上传状态 */
.attachment-file.is-uploading {
  opacity: 0.7;
}

.attachment-file.is-error {
  background: var(--el-color-danger-light-9);
  color: var(--el-color-danger);
}

.upload-percent {
  font-size: 11px;
  color: var(--el-color-primary);
  font-weight: 500;
}

/* 录音状态栏 */
.recording-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 12px;
  margin-bottom: 12px;
  border-radius: 12px;
  background: var(--el-color-danger-light-9);
}

.recording-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--el-color-danger);
  animation: pulse 1s infinite;
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.4;
  }
}

.recording-text {
  font-size: 13px;
  color: var(--el-text-color-secondary);
}

.recording-time {
  font-family: monospace;
  font-size: 14px;
  font-weight: 500;
  color: var(--el-text-color-primary);
}

.stop-btn {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 12px;
  border: none;
  border-radius: 6px;
  background: var(--el-color-danger);
  color: #fff;
  font-size: 12px;
  cursor: pointer;
  transition: opacity 0.2s;
}

.stop-btn:hover {
  opacity: 0.9;
}

/* 语音识别中状态栏 */
.transcribing-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px;
  margin-bottom: 12px;
  border-radius: 12px;
  background: var(--el-color-primary-light-9);
}

.transcribing-text {
  font-size: 13px;
  color: var(--el-color-primary);
}

/* 主输入容器 - 圆角边框包裹 */
.input-container {
  border: 1px solid var(--el-border-color);
  border-radius: 16px;
  background: var(--el-bg-color);
  overflow: hidden;
  transition: border-color 0.2s;
}

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

/* 输入框 */
.input-textarea {
  width: 100%;
  min-height: 48px;
  max-height: 150px;
  padding: 16px 16px 8px;
  border: none;
  background: transparent;
  color: var(--el-text-color-primary);
  font-size: 14px;
  line-height: 1.5;
  resize: none;
  outline: none;
}

.input-textarea::placeholder {
  color: var(--el-text-color-placeholder);
}

.input-textarea:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* 底部工具栏 */
.input-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
}

.toolbar-left,
.toolbar-right {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* 工具栏按钮 */
.toolbar-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--el-text-color-secondary);
  cursor: pointer;
  transition: all 0.2s;
}

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

.toolbar-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.toolbar-btn.is-recording {
  background: var(--el-color-danger);
  color: #fff;
}

.toolbar-btn.is-recording:hover {
  background: var(--el-color-danger-dark-2);
}

/* 发送按钮 */
.send-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  transition: all 0.2s;
}

.send-btn:not(.can-send):not(.is-stop) {
  background: var(--el-fill-color);
  color: var(--el-text-color-placeholder);
  cursor: not-allowed;
}

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

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

.send-btn.is-stop {
  background: var(--el-color-danger);
  color: #fff;
}

.send-btn.is-stop:hover {
  background: var(--el-color-danger-dark-2);
}
</style>
