<script setup>
import { ref, computed, nextTick, onUnmounted } from "vue";
import { useRouter } from "vue-router";
import {
  PROBLEM_API_URL,
  PROBLEM_MODEL,
  PROBLEM_API_KEY,
  IMAGE_MAX_SIZE_MB,
  IMAGE_ALLOWED_TYPES_EXAM,
} from "@/config/index.js";

const router = useRouter();

// API 配置
const API_KEY = PROBLEM_API_KEY;
const API_URL = PROBLEM_API_URL;
const MODEL = PROBLEM_MODEL;

// System Prompt - 引导式解题
const SYSTEM_PROMPT = `你是一位耐心的AI解题导师。你的核心任务是**引导学生自己思考**，而不是直接给出答案。

## 🚨 核心规则（必须严格遵守）：

1. **禁止直接给出完整答案或解题过程**
   - ❌ 错误：直接列出所有解题步骤和答案
   - ✅ 正确：一次只给出一个提示或引导性问题

2. **引导式教学流程**：
   - 第一步：帮助理解题目，提出"你觉得这道题在问什么？"
   - 第二步：引导学生识别关键信息，问"你注意到哪些重要条件？"
   - 第三步：提示解题方向，问"你觉得应该从哪里入手？"
   - 后续：根据学生的回答逐步引导，每次只给一个方向的提示

3. **互动方式**：
   - 每次回复最多给出一个解题思路提示
   - 必须以问题结尾，引导学生思考下一步
   - 对学生的回答给予鼓励和反馈
   - 如果学生卡住，可以给出更具体的提示，但仍不能直接给答案

4. **输出格式**：
   - 使用加粗 **强调重点**
   - 可以使用列表展示思考方向
   - 每次回复不超过100字（不含格式符号）
   - 必须以引导性问题结尾

## 示例对话：

**学生**："已知 f(x) = x² - 2x + 1，求 f(3) 的值。"

**你的回复**："好的！让我们一起来看这道题。

首先，你觉得这个表达式 f(x) = x² - 2x + 1 代表什么意思？**f(3)** 又表示什么呢？"

---

记住：你的目标是教会学生**如何思考**，而不是告诉他们答案！`;

// ── 状态管理 ──
const activeTab = ref("text"); // 'text' | 'image'
const textInput = ref(""); // 左侧面板的文本输入
const quickInput = ref(""); // 右侧快速追问输入框
const imageFile = ref(null); // { dataUrl, base64, mimeType }
const isDragOver = ref(false);
const fileInputRef = ref(null);
const messages = ref([]); // 对话历史
const isStreaming = ref(false);
const errorMsg = ref("");
const messagesContainer = ref(null);
const quickInputRef = ref(null);
const cancelTokenSource = ref(null);
let msgIdCounter = 0;

// ── 图片处理 ──
const readFileAsBase64 = (file) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      const dataUrl = e.target.result;
      resolve({ dataUrl, base64: dataUrl.split(",")[1], mimeType: file.type });
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });

const handleFile = async (file) => {
  if (!file) return;
  const allowed = IMAGE_ALLOWED_TYPES_EXAM;
  if (!allowed.includes(file.type)) {
    errorMsg.value = "仅支持 JPG / PNG / WebP 格式的图片";
    return;
  }
  if (file.size > IMAGE_MAX_SIZE_MB * 1024 * 1024) {
    errorMsg.value = `图片大小不能超过 ${IMAGE_MAX_SIZE_MB}MB`;
    return;
  }
  errorMsg.value = "";
  const { dataUrl, base64, mimeType } = await readFileAsBase64(file);
  imageFile.value = { dataUrl, base64, mimeType };
};

const onFileChange = (e) => handleFile(e.target.files[0]);
const onDrop = (e) => {
  isDragOver.value = false;
  handleFile(e.dataTransfer.files[0]);
};
const resetImage = () => {
  imageFile.value = null;
  if (fileInputRef.value) fileInputRef.value.value = "";
};

// ── 构建请求体（OpenAI 兼容格式）──
const buildRequestBody = (
  userMessage,
  imageBase64 = null,
  mimeType = "image/jpeg"
) => {
  const userContent = [];

  // 构建用户内容（OpenAI 格式）
  if (imageBase64) {
    userContent.push({
      type: "image_url",
      image_url: {
        url: `data:${mimeType};base64,${imageBase64}`,
      },
    });
  }

  userContent.push({
    type: "text",
    text: userMessage,
  });

  // 构建对话历史（OpenAI 兼容格式）
  const conversationHistory = [
    // 添加 system 消息
    {
      role: "system",
      content: SYSTEM_PROMPT,
    },
    // 添加历史消息
    ...messages.value.map((msg) => ({
      role: msg.role,
      content:
        msg.role === "user" && msg.imageContent
          ? msg.imageContent.map((item) => {
              if (item.type === "input_image") {
                // 转换为 OpenAI 格式
                return {
                  type: "image_url",
                  image_url: { url: item.image_url },
                };
              }
              return { type: "text", text: item.text };
            })
          : typeof msg.content === "string"
          ? msg.content
          : msg.content[0]?.text || "",
    })),
    // 添加当前用户消息
    {
      role: "user",
      content: userContent,
    },
  ];

  return {
    model: MODEL,
    messages: conversationHistory,
    stream: true,
  };
};

// ── 提取 SSE delta 文本 ──
const extractTextDelta = (chunk) => {
  let result = "";
  const lines = chunk.split("\n");
  for (const line of lines) {
    const trimmedLine = line.trim();
    if (!trimmedLine) continue;

    // 处理 SSE 格式: data: {...}
    if (trimmedLine.startsWith("data: ")) {
      const jsonStr = trimmedLine.slice(6).trim();
      if (!jsonStr || jsonStr === "[DONE]") continue;
      try {
        const data = JSON.parse(jsonStr);
        // OpenAI 兼容格式
        const text = data?.choices?.[0]?.delta?.content;
        if (text) result += text;
      } catch {
        // 忽略解析失败的行
      }
    }
    // 兼容非 SSE 格式（某些 API 直接返回 JSON 数组）
    else if (trimmedLine.startsWith("{")) {
      try {
        const data = JSON.parse(trimmedLine);
        const text = data?.choices?.[0]?.delta?.content || data?.choices?.[0]?.text;
        if (text) result += text;
      } catch {
        // 忽略解析失败的行
      }
    }
  }
  return result;
};

// ── 过滤思考块 ──
const filterThinkBlocks = (text) => {
  let filtered = text.replace(/<think class="min\[\\s\\S\]*?<\/think>/g, "");
  filtered = filtered.replace(/<think class="min\[\\s\\S\]*$/, "");
  return filtered;
};

// ── 发送消息 ──
const canSend = computed(() => {
  if (isStreaming.value) return false;
  if (activeTab.value === "text") return textInput.value.trim().length > 0;
  return imageFile.value !== null;
});

// 主发送函数（支持左侧主输入和右侧快速输入）
const sendMessage = async (isQuickInput = false) => {
  // 根据输入来源决定使用哪个输入值
  let userMessage = "";
  let currentImageBase64 = null;
  let currentMimeType = "image/jpeg";

  if (isQuickInput) {
    // 右侧快速输入框
    userMessage = quickInput.value.trim();
    if (!userMessage) return;
  } else {
    // 左侧主输入
    userMessage = textInput.value.trim() || "请帮我解答这道题";
    if (!canSend.value && !userMessage) return;
    currentImageBase64 = imageFile.value?.base64;
    currentMimeType = imageFile.value?.mimeType;
  }

  // 添加用户消息
  const userMsgId = ++msgIdCounter;
  const userMsg = {
    id: userMsgId,
    role: "user",
    content: userMessage,
    imageContent: null,
  };

  // 只有左侧主输入才处理图片
  if (!isQuickInput && activeTab.value === "image" && imageFile.value) {
    userMsg.imageContent = [
      {
        type: "image_url",
        image_url: {
          url: `data:${imageFile.value.mimeType};base64,${imageFile.value.base64}`,
        },
      },
      {
        type: "text",
        text: userMessage,
      },
    ];
    userMsg.hasImage = true;
    userMsg.imageUrl = imageFile.value.dataUrl;
  }

  messages.value.push(userMsg);

  // 清空输入（根据来源清空不同的输入框）
  if (isQuickInput) {
    quickInput.value = "";
  } else {
    textInput.value = "";
    if (activeTab.value === "image") {
      resetImage();
    }
  }

  await scrollToBottom();

  // 添加 AI 消息占位
  const aiMsgId = ++msgIdCounter;
  const aiMsg = {
    id: aiMsgId,
    role: "assistant",
    content: "",
    isLoading: true,
  };
  messages.value.push(aiMsg);

  isStreaming.value = true;
  errorMsg.value = "";

  // 使用 AbortController 替代 axios.CancelToken
  const abortController = new AbortController();
  cancelTokenSource.value = { cancel: () => abortController.abort() };

  try {
    // 使用 fetch 实现真正的流式输出
    const response = await fetch(API_URL, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${API_KEY}`,
      },
      body: JSON.stringify(
        buildRequestBody(userMessage, currentImageBase64, currentMimeType)
      ),
      signal: abortController.signal,
    });

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      throw new Error(errorData?.error?.message || `HTTP ${response.status}`);
    }

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = "";

    while (true) {
      const { done, value } = await reader.read();

      if (done) break;

      // 解码新的数据块
      buffer += decoder.decode(value, { stream: true });

      // 处理缓冲区中的完整行
      const lines = buffer.split("\n");
      buffer = lines.pop() || ""; // 保留不完整的行

      for (const line of lines) {
        const trimmedLine = line.trim();
        if (!trimmedLine || !trimmedLine.startsWith("data: ")) continue;

        const jsonStr = trimmedLine.slice(6).trim();
        if (!jsonStr || jsonStr === "[DONE]") continue;

        try {
          const data = JSON.parse(jsonStr);
          const delta = data?.choices?.[0]?.delta?.content;

          if (delta) {
            const msg = messages.value.find((m) => m.id === aiMsgId);
            if (msg) {
              msg.content = filterThinkBlocks(msg.content + delta);
              msg.isLoading = false;
            }
            await scrollToBottom();
          }
        } catch {
          // 忽略解析失败的行
        }
      }
    }

    // 处理剩余的缓冲区
    if (buffer.trim()) {
      const delta = extractTextDelta(buffer);
      if (delta) {
        const msg = messages.value.find((m) => m.id === aiMsgId);
        if (msg) {
          msg.content = filterThinkBlocks(msg.content + delta);
          msg.isLoading = false;
        }
      }
    }
  } catch (err) {
    if (err.name === "AbortError") return;
    console.error(err);
    errorMsg.value = err.message || "请求失败，请稍后重试";
    const msg = messages.value.find((m) => m.id === aiMsgId);
    if (msg) {
      msg.content = "抱歉，我遇到了一些问题。请重试或换一种方式提问。";
      msg.isLoading = false;
    }
  } finally {
    isStreaming.value = false;
    await scrollToBottom();
    // AI 回答完成后，给予输入框焦点
    if (quickInputRef.value) {
      quickInputRef.value.focus();
    }
  }
};

// ── 滚动到底部 ──
const scrollToBottom = async () => {
  await nextTick();
  if (messagesContainer.value) {
    messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight;
  }
};

// ── 增强 Markdown 渲染 ──
const renderContent = (text) => {
  if (!text) return "";

  let html = text;

  // 1. 代码块（必须先处理，避免被其他规则干扰）
  html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, _lang, code) => {
    const escapedCode = code
      .trim()
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;");
    return `<pre class="code-block"><code>${escapedCode}</code></pre>`;
  });

  // 2. 行内代码
  html = html.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>');

  // 3. 标题（## 步骤N 优先匹配）
  html = html.replace(
    /##\s*(步骤\d+[：:].+?)(\n|$)/g,
    '<div class="step-title">$1</div>'
  );

  // 4. 普通标题
  html = html.replace(/^###\s*(.+)$/gm, '<h4 class="md-h4">$1</h4>');
  html = html.replace(/^##\s*(.+)$/gm, '<h3 class="md-h3">$1</h3>');
  html = html.replace(/^#\s*(.+)$/gm, '<h2 class="md-h2">$1</h2>');

  // 5. 有序列表
  html = html.replace(/^\d+\.\s+(.+)$/gm, '<li class="ordered-item">$1</li>');
  html = html.replace(
    /(<li class="ordered-item">[\s\S]*?<\/li>\n?)+/g,
    '<ol class="ordered-list">$&</ol>'
  );

  // 6. 无序列表
  html = html.replace(/^[-*]\s+(.+)$/gm, '<li class="unordered-item">$1</li>');
  html = html.replace(
    /(<li class="unordered-item">[\s\S]*?<\/li>\n?)+/g,
    '<ul class="unordered-list">$&</ul>'
  );

  // 7. 加粗和斜体
  html = html.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
  html = html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
  html = html.replace(/\*(.+?)\*/g, "<em>$1</em>");

  // 8. 换行（最后处理）
  html = html.replace(/\n/g, "<br>");

  // 9. 清理多余的 <br>（在块级元素后）
  html = html.replace(/(<\/(?:pre|h[234]|ol|ul|div)>)<br>/g, "$1");

  return html;
};

const goBack = () => router.back();

const handleKeydown = (e) => {
  if (e.key === "Enter" && !e.shiftKey) {
    e.preventDefault();
    sendMessage(false); // 左侧主输入
  }
};

const handleQuickKeydown = (e) => {
  if (e.key === "Enter" && !e.shiftKey) {
    e.preventDefault();
    sendMessage(true); // 右侧快速输入
  }
};

onUnmounted(() => {
  if (cancelTokenSource.value) {
    cancelTokenSource.value.cancel("组件卸载，取消请求");
  }
});
</script>

<template>
  <div class="page-container">
    <!-- Nav Bar -->
    <div class="nav-bar">
      <button class="back-btn" @click="goBack">
        <svg
          width="20"
          height="20"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2.2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <polyline points="15 18 9 12 15 6" />
        </svg>
        <span>返回</span>
      </button>
      <h1 class="nav-title">AI 智能解题</h1>
      <div style="width: 72px"></div>
    </div>

    <!-- Error Banner -->
    <div v-if="errorMsg" class="error-banner">
      <span>{{ errorMsg }}</span>
      <button class="error-close" @click="errorMsg = ''">✕</button>
    </div>

    <!-- Main Layout: Left + Right -->
    <div class="main-layout">
      <!-- Left Panel: Input -->
      <div class="left-panel">
        <!-- Input Card -->
        <div class="input-card">
          <!-- Tabs -->
          <div class="tabs">
            <button
              class="tab-btn"
              :class="{ active: activeTab === 'text' }"
              @click="activeTab = 'text'"
            >
              <svg
                width="16"
                height="16"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <path
                  d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
                />
                <polyline points="14 2 14 8 20 8" />
                <line x1="16" y1="13" x2="8" y2="13" />
                <line x1="16" y1="17" x2="8" y2="17" />
                <polyline points="10 9 9 9 8 9" />
              </svg>
              文本输入
            </button>
            <button
              class="tab-btn"
              :class="{ active: activeTab === 'image' }"
              @click="activeTab = 'image'"
            >
              <svg
                width="16"
                height="16"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <rect x="3" y="3" width="18" height="18" rx="3" />
                <circle cx="8.5" cy="8.5" r="1.5" />
                <polyline points="21 15 16 10 5 21" />
              </svg>
              图片上传
            </button>
          </div>

          <!-- Text Input -->
          <div v-if="activeTab === 'text'" class="tab-content">
            <div class="text-input-header">
              <button
                class="example-btn"
                @click="
                  textInput = `Choose the correct answer:
The teacher asked the students _____ they had finished their homework.
A. that  B. if  C. what  D. whether`
                "
              >
                示例题目
              </button>
            </div>
            <textarea
              v-model="textInput"
              class="text-area"
              placeholder="请输入题目内容"
              :disabled="isStreaming"
              @keydown="handleKeydown"
            ></textarea>
            <div class="char-count">{{ textInput.length }} 字符</div>
          </div>

          <!-- Image Upload -->
          <div v-else class="tab-content">
            <div
              v-if="!imageFile"
              class="upload-zone"
              :class="{ 'drag-over': isDragOver }"
              @click="fileInputRef.click()"
              @dragover.prevent="isDragOver = true"
              @dragleave.prevent="isDragOver = false"
              @drop.prevent="onDrop"
            >
              <input
                ref="fileInputRef"
                type="file"
                accept="image/jpeg,image/jpg,image/png,image/webp"
                style="display: none"
                @change="onFileChange"
              />
              <div class="upload-icon-wrap">
                <svg
                  width="36"
                  height="36"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="1.5"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                >
                  <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
                  <polyline points="17 8 12 3 7 8" />
                  <line x1="12" y1="3" x2="12" y2="15" />
                </svg>
              </div>
              <p class="upload-title">点击或拖拽上传试题图片</p>
              <p class="upload-hint">支持 JPG、PNG、WebP，最大 10MB</p>
            </div>
            <div v-else class="image-preview-wrap">
              <div class="image-preview-header">
                <span class="preview-label">已上传图片</span>
                <button
                  class="reupload-btn"
                  @click="resetImage"
                  :disabled="isStreaming"
                >
                  重新上传
                </button>
              </div>
              <img :src="imageFile.dataUrl" class="preview-img" />
            </div>
          </div>
        </div>

        <!-- Send Button -->
        <div class="btn-wrap">
          <button
            class="send-btn"
            :disabled="!canSend"
            @click="sendMessage(false)"
          >
            <span v-if="isStreaming" class="btn-spinner"></span>
            <svg
              v-else
              width="20"
              height="20"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                stroke-linecap="round"
                stroke-linejoin="round"
                d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5"
              />
            </svg>
            <span>{{ isStreaming ? "思考中..." : "开始解题" }}</span>
          </button>
        </div>
      </div>

      <!-- Right Panel: Dialog -->
      <div class="right-panel">
        <!-- Empty State -->
        <div v-if="messages.length === 0" class="empty-state">
          <div class="empty-icon">
            <svg
              width="48"
              height="48"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="1.2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <circle cx="12" cy="12" r="10" />
              <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
              <line x1="12" y1="17" x2="12.01" y2="17" />
            </svg>
          </div>
          <p class="empty-title">准备就绪</p>
          <p class="empty-hint">在左侧输入题目，AI 将一步步引导你思考和解题</p>
        </div>

        <!-- Messages Area -->
        <div v-else class="messages-area" ref="messagesContainer">
          <div
            v-for="msg in messages"
            :key="msg.id"
            class="message-item"
            :class="msg.role"
          >
            <!-- User Message -->
            <div v-if="msg.role === 'user'" class="user-message">
              <div v-if="msg.hasImage" class="user-image">
                <img :src="msg.imageUrl" alt="题目图片" />
              </div>
              <div class="user-text">{{ msg.content }}</div>
            </div>

            <!-- AI Message -->
            <div v-else class="ai-message">
              <div class="ai-avatar">🤖</div>
              <div class="ai-content">
                <div v-if="msg.isLoading" class="loading-dots">
                  <span></span>
                  <span></span>
                  <span></span>
                </div>
                <div
                  v-else
                  class="ai-text"
                  v-html="renderContent(msg.content)"
                ></div>
              </div>
            </div>
          </div>
        </div>

        <!-- Quick Input for follow-up questions (Fixed at bottom) -->
        <div v-if="messages.length > 0" class="quick-input">
          <input
            ref="quickInputRef"
            v-model="quickInput"
            type="text"
            class="quick-input-box"
            placeholder="继续提问或输入你的想法..."
            :disabled="isStreaming"
            @keydown="handleQuickKeydown"
          />
          <button
            class="quick-send-btn"
            :disabled="isStreaming || !quickInput.trim()"
            @click="sendMessage(true)"
          >
            <svg
              width="18"
              height="18"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
            >
              <path
                stroke-linecap="round"
                stroke-linejoin="round"
                d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5"
              />
            </svg>
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.page-container {
  height: 100vh;
  overflow: hidden;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  font-family: "Inter", "PingFang SC", -apple-system, sans-serif;
}

/* Nav */
.nav-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.5rem;
  background: rgba(255, 255, 255, 0.03);
  border-bottom: 1px solid var(--card-border);
  backdrop-filter: blur(12px);
  position: sticky;
  top: 0;
  z-index: 10;
}

.back-btn {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 0.95rem;
  padding: 0.4rem 0.75rem;
  border-radius: 8px;
  transition: all 0.2s;
}

.back-btn:hover {
  background: rgba(255, 255, 255, 0.07);
  color: var(--text-primary);
}

.nav-title {
  font-size: 1.2rem;
  font-weight: 600;
  margin: 0;
  background: linear-gradient(135deg, #8b5cf6, #7c3aed);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* Main Layout */
.main-layout {
  flex: 1;
  min-height: 0;
  display: flex;
  overflow: hidden;
}

/* Left Panel */
.left-panel {
  width: 420px;
  min-width: 320px;
  max-width: 480px;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.5rem;
  border-right: 1px solid var(--card-border);
  overflow-y: auto;
  flex-shrink: 0;
}

/* Right Panel */
.right-panel {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  position: relative;
}

/* Error Banner */
.error-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: rgba(239, 68, 68, 0.12);
  border: 1px solid rgba(239, 68, 68, 0.3);
  border-bottom: 1px solid rgba(239, 68, 68, 0.3);
  padding: 0.75rem 1.5rem;
  color: #fca5a5;
  font-size: 0.9rem;
  animation: fadeInUp 0.2s ease;
  flex-shrink: 0;
}

.error-close {
  background: none;
  border: none;
  color: #fca5a5;
  cursor: pointer;
  font-size: 1rem;
  opacity: 0.7;
  transition: opacity 0.2s;
}

.error-close:hover {
  opacity: 1;
}

/* Input Card */
.input-card {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--card-border);
  border-radius: 20px;
  overflow: hidden;
  backdrop-filter: blur(12px);
}

/* Tabs */
.tabs {
  display: flex;
  border-bottom: 1px solid var(--card-border);
}

.tab-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 1rem;
  background: none;
  border: none;
  color: var(--text-secondary);
  font-size: 0.95rem;
  cursor: pointer;
  transition: all 0.2s;
  position: relative;
}

.tab-btn::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(135deg, #8b5cf6, #7c3aed);
  transform: scaleX(0);
  transition: transform 0.25s;
}

.tab-btn.active {
  color: #8b5cf6;
}

.tab-btn.active::after {
  transform: scaleX(1);
}

.tab-btn:hover:not(.active) {
  color: var(--text-primary);
  background: rgba(255, 255, 255, 0.03);
}

/* Tab Content */
.tab-content {
  padding: 1.25rem;
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.text-area {
  width: 100%;
  flex: 1;
  min-height: 260px;
  background: rgba(0, 0, 0, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 12px;
  color: var(--text-primary);
  font-size: 0.95rem;
  line-height: 1.7;
  padding: 1rem;
  resize: none;
  outline: none;
  transition: border-color 0.2s, box-shadow 0.2s;
  box-sizing: border-box;
  font-family: inherit;
}

.text-area::placeholder {
  color: var(--text-secondary);
  opacity: 0.6;
}

.text-area:focus {
  border-color: rgba(139, 92, 246, 0.5);
  box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1);
}

.text-area:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.char-count {
  text-align: right;
  font-size: 0.8rem;
  color: var(--text-secondary);
  margin-top: 0.5rem;
  opacity: 0.6;
}

/* Text Input Header */
.text-input-header {
  display: flex;
  align-items: center;
  margin-bottom: 0.75rem;
}

.example-btn {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: rgba(139, 92, 246, 0.1);
  border: 1px solid rgba(139, 92, 246, 0.25);
  color: #8b5cf6;
  border-radius: 8px;
  padding: 0.35rem 0.85rem;
  font-size: 0.85rem;
  cursor: pointer;
  transition: all 0.2s;
}

.example-btn:hover {
  background: rgba(139, 92, 246, 0.2);
  transform: translateY(-1px);
}

/* Upload Zone */
.upload-zone {
  border: 2px dashed rgba(139, 92, 246, 0.3);
  border-radius: 16px;
  padding: 3rem 2rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  cursor: pointer;
  transition: all 0.25s;
  text-align: center;
}

.upload-zone:hover,
.upload-zone.drag-over {
  border-color: #8b5cf6;
  background: rgba(139, 92, 246, 0.06);
  transform: translateY(-2px);
  box-shadow: 0 8px 32px rgba(139, 92, 246, 0.1);
}

.upload-icon-wrap {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: rgba(139, 92, 246, 0.1);
  border: 1px solid rgba(139, 92, 246, 0.25);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #8b5cf6;
  transition: all 0.25s;
}

.upload-zone:hover .upload-icon-wrap,
.upload-zone.drag-over .upload-icon-wrap {
  background: rgba(139, 92, 246, 0.18);
  transform: scale(1.08);
}

.upload-title {
  font-size: 1rem;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0;
}

.upload-hint {
  font-size: 0.85rem;
  color: var(--text-secondary);
  margin: 0;
}

/* Image Preview */
.image-preview-wrap {
  background: rgba(0, 0, 0, 0.15);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  overflow: hidden;
}

.image-preview-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--card-border);
}

.preview-label {
  font-size: 0.875rem;
  color: var(--text-secondary);
}

.reupload-btn {
  background: rgba(139, 92, 246, 0.1);
  border: 1px solid rgba(139, 92, 246, 0.25);
  color: #8b5cf6;
  border-radius: 8px;
  padding: 0.3rem 0.75rem;
  font-size: 0.85rem;
  cursor: pointer;
  transition: all 0.2s;
}

.reupload-btn:hover:not(:disabled) {
  background: rgba(139, 92, 246, 0.2);
}

.reupload-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.preview-img {
  width: 100%;
  max-height: 360px;
  object-fit: contain;
  display: block;
  background: rgba(0, 0, 0, 0.2);
}

/* Send Button */
.btn-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
}

.send-btn {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: linear-gradient(135deg, #8b5cf6, #7c3aed);
  border: none;
  color: white;
  font-size: 1rem;
  font-weight: 600;
  padding: 0.875rem 2.5rem;
  border-radius: 50px;
  cursor: pointer;
  transition: all 0.25s;
  box-shadow: 0 4px 20px rgba(139, 92, 246, 0.35);
}

.send-btn:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 8px 28px rgba(139, 92, 246, 0.5);
}

.send-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

.btn-spinner {
  width: 18px;
  height: 18px;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: white;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

/* Empty State */
.empty-state {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  color: var(--text-secondary);
  opacity: 0.5;
  padding: 1.5rem;
}

.empty-icon {
  color: var(--text-secondary);
  opacity: 0.4;
}

.empty-title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0;
  color: var(--text-primary);
}

.empty-hint {
  font-size: 0.875rem;
  margin: 0;
  text-align: center;
}

/* Messages Area */
.messages-area {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  min-height: 0;
}

/* 自定义滚动条样式 */
.messages-area::-webkit-scrollbar {
  width: 6px;
}

.messages-area::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.02);
  border-radius: 3px;
}

.messages-area::-webkit-scrollbar-thumb {
  background: rgba(139, 92, 246, 0.3);
  border-radius: 3px;
  transition: background 0.2s;
}

.messages-area::-webkit-scrollbar-thumb:hover {
  background: rgba(139, 92, 246, 0.5);
}

.message-item {
  animation: fadeInUp 0.3s ease;
}

/* User Message */
.user-message {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
}

.user-image {
  max-width: 300px;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid rgba(139, 92, 246, 0.3);
}

.user-image img {
  width: 100%;
  display: block;
}

.user-text {
  background: linear-gradient(135deg, #8b5cf6, #7c3aed);
  color: white;
  padding: 0.875rem 1.25rem;
  border-radius: 18px 18px 4px 18px;
  max-width: 70%;
  font-size: 0.95rem;
  line-height: 1.6;
}

/* AI Message */
.ai-message {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
}

.ai-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(139, 92, 246, 0.15);
  border: 1px solid rgba(139, 92, 246, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
  flex-shrink: 0;
}

.ai-content {
  flex: 1;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--card-border);
  border-radius: 18px;
  padding: 1rem 1.25rem;
}

.ai-text {
  font-size: 0.95rem;
  color: var(--text-primary);
  line-height: 1.8;
}

.ai-text :deep(strong) {
  color: #8b5cf6;
  font-weight: 600;
}

.ai-text :deep(.step-title) {
  font-weight: 600;
  color: #8b5cf6;
  margin: 1rem 0 0.5rem 0;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid rgba(139, 92, 246, 0.2);
}

/* Markdown 样式 */
.ai-text :deep(.md-h2) {
  font-size: 1.25rem;
  font-weight: 600;
  color: #8b5cf6;
  margin: 1rem 0 0.75rem 0;
}

.ai-text :deep(.md-h3) {
  font-size: 1.1rem;
  font-weight: 600;
  color: #a78bfa;
  margin: 0.875rem 0 0.625rem 0;
}

.ai-text :deep(.md-h4) {
  font-size: 1rem;
  font-weight: 600;
  color: #c4b5fd;
  margin: 0.75rem 0 0.5rem 0;
}

.ai-text :deep(em) {
  color: #c4b5fd;
  font-style: italic;
}

/* 代码块 */
.ai-text :deep(.code-block) {
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid rgba(139, 92, 246, 0.3);
  border-radius: 8px;
  padding: 1rem;
  margin: 0.75rem 0;
  overflow-x: auto;
  font-family: "Consolas", "Monaco", monospace;
  font-size: 0.875rem;
}

.ai-text :deep(.code-block code) {
  color: #e2e8f0;
  background: none;
  padding: 0;
}

/* 行内代码 */
.ai-text :deep(.inline-code) {
  background: rgba(139, 92, 246, 0.15);
  border: 1px solid rgba(139, 92, 246, 0.3);
  color: #c4b5fd;
  padding: 0.15rem 0.4rem;
  border-radius: 4px;
  font-family: "Consolas", "Monaco", monospace;
  font-size: 0.9em;
}

/* 列表 */
.ai-text :deep(.ordered-list),
.ai-text :deep(.unordered-list) {
  margin: 0.5rem 0;
  padding-left: 1.5rem;
}

.ai-text :deep(.ordered-list) {
  list-style: decimal;
}

.ai-text :deep(.unordered-list) {
  list-style: disc;
}

.ai-text :deep(li) {
  margin: 0.25rem 0;
  line-height: 1.6;
}

/* Loading dots */
.loading-dots {
  display: flex;
  gap: 5px;
  align-items: center;
  padding: 0.25rem 0;
}

.loading-dots span {
  width: 8px;
  height: 8px;
  background: #8b5cf6;
  border-radius: 50%;
  animation: bounce 1.2s ease-in-out infinite;
}

.loading-dots span:nth-child(2) {
  animation-delay: 0.2s;
}

.loading-dots span:nth-child(3) {
  animation-delay: 0.4s;
}

/* Quick Input */
.quick-input {
  display: flex;
  gap: 0.75rem;
  padding: 1rem 1.5rem;
  background: rgba(255, 255, 255, 0.02);
  border-top: 1px solid var(--card-border);
  flex-shrink: 0;
}

.quick-input-box {
  flex: 1;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  padding: 0.75rem 1rem;
  color: var(--text-primary);
  font-size: 0.95rem;
  outline: none;
  transition: border-color 0.2s;
}

.quick-input-box:focus {
  border-color: rgba(139, 92, 246, 0.5);
}

.quick-input-box::placeholder {
  color: var(--text-secondary);
  opacity: 0.6;
}

.quick-input-box:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background: rgba(255, 255, 255, 0.01);
}

.quick-send-btn {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: linear-gradient(135deg, #8b5cf6, #7c3aed);
  border: none;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s;
  flex-shrink: 0;
}

.quick-send-btn:hover:not(:disabled) {
  transform: scale(1.05);
}

.quick-send-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Animations */
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes bounce {
  0%,
  80%,
  100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-6px);
  }
}

@media (max-width: 768px) {
  .main-layout {
    flex-direction: column;
  }

  .left-panel {
    width: 100%;
    max-width: 100%;
    border-right: none;
    border-bottom: 1px solid var(--card-border);
    overflow-y: visible;
  }

  .right-panel {
    min-height: 400px;
  }

  .messages-area {
    padding: 1rem;
  }

  .quick-input {
    padding: 0.75rem 1rem;
  }

  .upload-zone {
    padding: 2rem 1rem;
  }

  .send-btn {
    padding: 0.875rem 1.75rem;
  }

  .user-text {
    max-width: 85%;
  }
}
</style>
