"use client";

import Image from "next/image";
import React, { useMemo } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { AnimatePresence, motion } from "framer-motion";
import {
  Avatar,
  Box,
  Button,
  Checkbox,
  Chip,
  CircularProgress,
  Collapse,
  FormControlLabel,
  IconButton,
  Paper,
  Stack,
  TextField,
  Tooltip,
  Typography,
  alpha,
  useTheme,
} from "@mui/material";
import type { Theme } from "@mui/material/styles";
import ContentCopyRounded from "@mui/icons-material/ContentCopyRounded";
import RefreshRounded from "@mui/icons-material/RefreshRounded";
import { TbArrowsSplit2 } from "react-icons/tb";
import {
  parseAssistantMessageSections,
  parseContentWithToolCalls,
  type ContentSegment,
} from "./chatMessageSections";
import markdownStyles from "./GlobalChatboxMarkdown.module.css";
import type { Message, SpeechState } from "./GlobalChatbox.types";
import { stripMarkdown } from "./GlobalChatbox.utils";
import { AgentProgressTimeline } from "./AgentProgressTimeline";
import { ChatInlineChart } from "./ChatInlineChart";
import type { ChatChartSeries } from "./ChatInlineChart";
import { ChatToolCallBlock } from "./ChatToolCallBlock";
import { AgentArtifactPanel } from "./AgentArtifactPanel";
import ErrorOutlineRounded from "@mui/icons-material/ErrorOutlineRounded";
import VolumeUpRounded from "@mui/icons-material/VolumeUpRounded";
import PauseRounded from "@mui/icons-material/PauseRounded";
import PlayArrowRounded from "@mui/icons-material/PlayArrowRounded";
import StopRounded from "@mui/icons-material/StopRounded";
import VerifiedUserRounded from "@mui/icons-material/VerifiedUserRounded";
import TerminalRounded from "@mui/icons-material/TerminalRounded";
import FolderOpenRounded from "@mui/icons-material/FolderOpenRounded";
import CheckCircleRounded from "@mui/icons-material/CheckCircleRounded";
import BlockRounded from "@mui/icons-material/BlockRounded";
import PushPinRounded from "@mui/icons-material/PushPinRounded";
import EditNoteRounded from "@mui/icons-material/EditNoteRounded";
import KeyboardArrowDownRounded from "@mui/icons-material/KeyboardArrowDownRounded";
import KeyboardArrowUpRounded from "@mui/icons-material/KeyboardArrowUpRounded";
import AssignmentTurnedInRounded from "@mui/icons-material/AssignmentTurnedInRounded";
import HelpOutlineRounded from "@mui/icons-material/HelpOutlineRounded";
import RadioButtonUncheckedRounded from "@mui/icons-material/RadioButtonUncheckedRounded";
import type { PermissionReply } from "@/lib/chatStream";

type AgentTurnProps = {
  message: Message;
  messageSpeechState: SpeechState;
  onSpeak: (messageId: string, text: string) => void;
  onPause: () => void;
  onResume: () => void;
  onStopSpeech: () => void;
  isTtsSupported: boolean;
  onRegenerate: (messageId: string) => void;
  onCreateBranch: (messageId: string) => void;
  onReplyPermission: (requestId: string, reply: PermissionReply) => void;
  onReplyQuestion: (requestId: string, answers: string[][]) => void;
  onRejectQuestion: (requestId: string) => void;
};

const normalizeClipboardText = (value: string) => value.replace(/\s+$/u, "");

const MarkdownBlock = ({ children }: { children: string }) => {
  const handleCopy = React.useCallback((event: React.ClipboardEvent<HTMLDivElement>) => {
    const selectedText = window.getSelection()?.toString();
    if (!selectedText) return;

    event.preventDefault();
    event.clipboardData.setData("text/plain", normalizeClipboardText(selectedText));
  }, []);

  return (
    <div className={markdownStyles.markdown} onCopy={handleCopy}>
      <ReactMarkdown remarkPlugins={[remarkGfm]}>{children}</ReactMarkdown>
    </div>
  );
};

const formatMetadataValue = (value: unknown) => {
  if (typeof value === "string") {
    return value;
  }
  try {
    return JSON.stringify(value);
  } catch {
    return "[unserializable]";
  }
};

const truncateText = (value: string, maxLength: number) =>
  value.length > maxLength ? `${value.slice(0, maxLength - 3)}...` : value;

const formatMetadata = (metadata: Record<string, unknown>) => {
  const entries = Object.entries(metadata)
    .filter(([key]) => !["command", "path", "file", "directory"].includes(key))
    .slice(0, 3);
  if (!entries.length) {
    return "";
  }
  return entries
    .map(([key, value]) => `${key}: ${truncateText(formatMetadataValue(value), 64)}`)
    .join("；");
};

const getPermissionTitle = (permission: NonNullable<Message["permissions"]>[number]) => {
  if (permission.permission === "external_directory") return "访问工作区外目录";
  if (permission.permission === "bash") return "执行终端命令";
  if (permission.permission === "edit") return "修改文件内容";
  return permission.permission || "工具权限请求";
};

const getPermissionPrimaryValue = (
  permission: NonNullable<Message["permissions"]>[number],
) => {
  const command = permission.metadata.command;
  if (typeof command === "string" && command.trim()) {
    return command.trim();
  }
  for (const key of ["path", "file", "directory"]) {
    const value = permission.metadata[key];
    if (typeof value === "string" && value.trim()) {
      return value.trim();
    }
  }
  return permission.patterns[0] ?? permission.permission;
};

const PermissionIcon = ({
  permission,
}: {
  permission: NonNullable<Message["permissions"]>[number];
}) => {
  if (permission.permission === "bash") {
    return <TerminalRounded sx={{ fontSize: 22 }} />;
  }
  if (permission.permission === "external_directory") {
    return <FolderOpenRounded sx={{ fontSize: 22 }} />;
  }
  return <VerifiedUserRounded sx={{ fontSize: 22 }} />;
};

const getPermissionStatusLabel = (status: NonNullable<Message["permissions"]>[number]["status"]) => {
  if (status === "approved_always") return "已始终允许";
  if (status === "approved_once") return "已允许一次";
  if (status === "rejected") return "已拒绝";
  if (status === "error") return "提交失败";
  if (status === "submitting") return "提交中";
  return "等待确认";
};

const pendingPermissionColor = "#f9a825";
const approvedOncePermissionColor = "#00838f";

const getPermissionStatusColor = (
  status: NonNullable<Message["permissions"]>[number]["status"],
  theme: Theme,
) => {
  if (status === "approved_once") return approvedOncePermissionColor;
  if (status === "approved_always") return theme.palette.success.main;
  if (status === "rejected" || status === "error") return theme.palette.error.main;
  return pendingPermissionColor;
};

const getPermissionStatusTextColor = (
  status: NonNullable<Message["permissions"]>[number]["status"],
  theme: Theme,
) => {
  if (status === "approved_once") return "#006c78";
  if (status === "approved_always") return theme.palette.success.dark;
  if (status === "rejected" || status === "error") return theme.palette.error.main;
  return "#8a5a00";
};

const PermissionRequestCard = ({
  permission,
  onReply,
}: {
  permission: NonNullable<Message["permissions"]>[number];
  onReply: (requestId: string, reply: PermissionReply) => void;
}) => {
  const theme = useTheme();
  const isPending = permission.status === "pending" || permission.status === "error";
  const isSubmitting = permission.status === "submitting";
  const primaryValue = getPermissionPrimaryValue(permission);
  const metadataText = formatMetadata(permission.metadata);
  const accentColor = getPermissionStatusColor(permission.status, theme);
  const statusTextColor = getPermissionStatusTextColor(permission.status, theme);
  const statusLabel = getPermissionStatusLabel(permission.status);

  return (
    <Box
      sx={{
        borderRadius: 3,
        overflow: "hidden",
        border: `1px solid ${alpha("#fff", 0.72)}`,
        bgcolor: alpha("#fff", 0.5),
        boxShadow: `0 8px 24px ${alpha("#000", 0.05)}`,
        backdropFilter: "blur(20px)",
        position: "relative",
        "&::before": {
          content: '""',
          position: "absolute",
          inset: "10px auto 10px 0",
          width: 3,
          borderRadius: "0 999px 999px 0",
          bgcolor: accentColor,
        },
      }}
    >
      <Stack
        direction="row"
        spacing={1}
        alignItems="center"
        sx={{
          px: 1.5,
          py: 1.25,
          pl: 1.75,
          borderBottom: `1px solid ${alpha("#000", 0.05)}`,
        }}
      >
        <Box
          sx={{
            width: 32,
            height: 32,
            borderRadius: "50%",
            display: "grid",
            placeItems: "center",
            flex: "0 0 auto",
            color: accentColor,
            bgcolor: alpha(accentColor, 0.1),
            border: `1px solid ${alpha(accentColor, 0.16)}`,
          }}
        >
          <PermissionIcon permission={permission} />
        </Box>
        <Box sx={{ minWidth: 0, flex: 1 }}>
          <Typography variant="subtitle2" fontWeight={800} noWrap sx={{ lineHeight: 1.25 }}>
            {getPermissionTitle(permission)}
          </Typography>
        </Box>
        <Chip
          size="small"
          label={statusLabel}
          sx={{
            height: 24,
            fontSize: "0.7rem",
            fontWeight: 800,
            borderRadius: "12px",
            bgcolor: alpha(accentColor, 0.12),
            color: statusTextColor,
            "& .MuiChip-label": { px: 1 },
          }}
        />
      </Stack>

      <Stack spacing={1.15} sx={{ px: 1.5, pt: 1.25, pb: 1.35, pl: 1.75 }}>
        <Box
          sx={{
            px: 1.25,
            py: 1,
            borderRadius: 2.5,
            bgcolor: alpha("#000", 0.025),
            border: `1px solid ${alpha("#000", 0.045)}`,
          }}
        >
          <Typography variant="caption" color="text.secondary" fontWeight={800}>
            请求目标
          </Typography>
          <Typography
            variant="body2"
            color="text.primary"
            fontFamily={permission.permission === "bash" ? "monospace" : undefined}
            sx={{
              mt: 0.25,
              lineHeight: 1.55,
              wordBreak: "break-word",
              whiteSpace: "pre-wrap",
            }}
          >
            {primaryValue}
          </Typography>
        </Box>

        {metadataText ? (
          <Typography variant="caption" color="text.secondary" sx={{ wordBreak: "break-word" }}>
            {metadataText}
          </Typography>
        ) : null}
      </Stack>

      {permission.error ? (
        <Box sx={{ px: 1.5, pb: isPending || isSubmitting ? 1 : 1.35, pl: 1.75 }}>
          <Typography
            variant="caption"
            color="error.main"
            sx={{
              display: "block",
              px: 1.25,
              py: 0.75,
              borderRadius: 2,
              bgcolor: alpha(theme.palette.error.main, 0.06),
              wordBreak: "break-word",
            }}
          >
            {permission.error}
          </Typography>
        </Box>
      ) : null}

      {isPending || isSubmitting ? (
        <Stack
          direction="row"
          spacing={1}
          flexWrap="wrap"
          useFlexGap
          sx={{ px: 1.5, pb: 1.35, pl: 1.75, pt: 0 }}
        >
            <Button
              size="small"
              variant="contained"
              disableElevation
              disabled={isSubmitting}
              onClick={() => onReply(permission.requestId, "once")}
              startIcon={
                isSubmitting ? (
                  <CircularProgress size={14} color="inherit" />
                ) : (
                  <CheckCircleRounded fontSize="small" />
                )
              }
              sx={{
                minWidth: 94,
                height: 34,
                borderRadius: "17px",
                bgcolor: "#00838f",
                fontWeight: 800,
                fontSize: "0.78rem",
                textTransform: "none",
                boxShadow: `0 4px 12px ${alpha("#00838f", 0.24)}`,
                "&:hover": {
                  bgcolor: "#006c78",
                  boxShadow: `0 6px 16px ${alpha("#00838f", 0.28)}`,
                },
              }}
            >
              允许一次
            </Button>
            <Button
              size="small"
              variant="outlined"
              disabled={isSubmitting}
              onClick={() => onReply(permission.requestId, "always")}
              startIcon={<PushPinRounded fontSize="small" />}
              sx={{
                height: 34,
                borderRadius: "17px",
                px: 1.5,
                fontWeight: 800,
                fontSize: "0.78rem",
                textTransform: "none",
                color: "#00838f",
                borderColor: alpha("#00838f", 0.24),
                bgcolor: alpha("#fff", 0.45),
                "&:hover": {
                  borderColor: alpha("#00838f", 0.36),
                  bgcolor: alpha("#00838f", 0.08),
                },
              }}
            >
              始终允许
            </Button>
            <Button
              size="small"
              color="error"
              variant="outlined"
              disabled={isSubmitting}
              onClick={() => onReply(permission.requestId, "reject")}
              startIcon={<BlockRounded fontSize="small" />}
              sx={{
                height: 34,
                borderRadius: "17px",
                px: 1.5,
                fontWeight: 800,
                fontSize: "0.78rem",
                textTransform: "none",
                borderColor: alpha(theme.palette.error.main, 0.22),
                bgcolor: alpha("#fff", 0.45),
                "&:hover": {
                  borderColor: alpha(theme.palette.error.main, 0.34),
                  bgcolor: alpha(theme.palette.error.main, 0.07),
                },
              }}
            >
              拒绝
            </Button>
        </Stack>
      ) : null}
    </Box>
  );
};

const PermissionRequestGroup = ({
  permissions,
  isRunning,
  onReply,
}: {
  permissions: NonNullable<Message["permissions"]>;
  isRunning: boolean;
  onReply: (requestId: string, reply: PermissionReply) => void;
}) => {
  const theme = useTheme();
  const onceCount = permissions.filter((permission) => permission.status === "approved_once").length;
  const alwaysCount = permissions.filter((permission) => permission.status === "approved_always").length;
  const rejectedCount = permissions.filter((permission) => permission.status === "rejected").length;
  const pendingCount = permissions.length - onceCount - alwaysCount - rejectedCount;
  const hasPendingPermissions = pendingCount > 0;
  const [expanded, setExpanded] = React.useState(false);
  const latestPermissions = permissions.slice(-3);
  const pendingPermissions = permissions.filter(
    (permission) =>
      permission.status === "pending" ||
      permission.status === "submitting" ||
      permission.status === "error",
  );
  const summaryItems = [
    { label: "共", value: permissions.length, color: theme.palette.text.secondary },
    { label: "允许一次", value: onceCount, color: getPermissionStatusColor("approved_once", theme), textColor: getPermissionStatusTextColor("approved_once", theme) },
    { label: "始终允许", value: alwaysCount, color: getPermissionStatusColor("approved_always", theme), textColor: getPermissionStatusTextColor("approved_always", theme) },
    { label: "拒绝", value: rejectedCount, color: getPermissionStatusColor("rejected", theme), textColor: getPermissionStatusTextColor("rejected", theme) },
  ];
  const chipColor = pendingCount > 0 ? getPermissionStatusColor("pending", theme) : rejectedCount > 0 ? getPermissionStatusColor("rejected", theme) : getPermissionStatusColor("approved_always", theme);
  const chipTextColor = pendingCount > 0 ? getPermissionStatusTextColor("pending", theme) : rejectedCount > 0 ? getPermissionStatusTextColor("rejected", theme) : getPermissionStatusTextColor("approved_always", theme);

  return (
    <Box
      sx={{
        borderRadius: 3,
        overflow: "hidden",
        border: `1px solid ${alpha("#fff", 0.72)}`,
        bgcolor: alpha("#fff", 0.46),
        boxShadow: `0 8px 24px ${alpha("#000", 0.045)}`,
        backdropFilter: "blur(20px)",
      }}
    >
      <Stack
        direction="row"
        alignItems="center"
        spacing={1}
        role="button"
        tabIndex={0}
        onClick={() => setExpanded((value) => !value)}
        onKeyDown={(event) => {
          if (event.key === "Enter" || event.key === " ") {
            event.preventDefault();
            setExpanded((value) => !value);
          }
        }}
        sx={{
          px: 1.5,
          py: 1.15,
          cursor: "pointer",
          transition: "background-color 0.2s ease",
          "&:hover": { bgcolor: alpha("#000", 0.025) },
        }}
      >
        <Box
          sx={{
            width: 30,
            height: 30,
            borderRadius: "50%",
            display: "grid",
            placeItems: "center",
            flex: "0 0 auto",
            color: chipColor,
            bgcolor: alpha(chipColor, 0.1),
            border: `1px solid ${alpha(chipColor, 0.15)}`,
          }}
        >
          <VerifiedUserRounded sx={{ fontSize: 18 }} />
        </Box>
        <Box sx={{ minWidth: 0, flex: 1 }}>
          <Typography variant="subtitle2" fontWeight={800} noWrap sx={{ lineHeight: 1.25 }}>
            权限请求
          </Typography>
          <Stack
            direction="row"
            flexWrap="wrap"
            gap={0.6}
            sx={{ mt: 0.55, maxHeight: 48, overflow: "hidden" }}
          >
            {summaryItems.map((item) => (
              <Box
                key={item.label}
                component="span"
                sx={{
                  display: "inline-flex",
                  alignItems: "center",
                  gap: 0.45,
                  height: 22,
                  px: 0.8,
                  borderRadius: "11px",
                  bgcolor: alpha(item.color, 0.08),
                  border: `1px solid ${alpha(item.color, 0.12)}`,
                  color: "textColor" in item ? item.textColor : item.color,
                  fontSize: "0.7rem",
                  fontWeight: 800,
                  lineHeight: 1,
                  whiteSpace: "nowrap",
                }}
              >
                <Box
                  component="span"
                  sx={{
                    color: "textColor" in item ? item.textColor : item.color,
                    fontWeight: 700,
                  }}
                >
                  {item.label}
                </Box>
                <Box component="span">{item.value} 项</Box>
              </Box>
            ))}
          </Stack>
        </Box>
        {isRunning && pendingCount > 0 ? (
          <Chip
            size="small"
            label={`待确认 ${pendingCount} 项`}
            sx={{
              height: 24,
              borderRadius: "12px",
              fontSize: "0.7rem",
              fontWeight: 800,
              color: chipTextColor,
              bgcolor: alpha(chipColor, 0.1),
              "& .MuiChip-label": { px: 1 },
            }}
          />
        ) : null}
        <IconButton
          size="small"
          aria-label={expanded ? "收起权限请求" : "展开权限请求"}
          sx={{
            width: 28,
            height: 28,
            color: "text.secondary",
            bgcolor: alpha("#000", 0.035),
            "&:hover": { bgcolor: alpha("#000", 0.07) },
          }}
        >
          {expanded ? (
            <KeyboardArrowUpRounded sx={{ fontSize: 18 }} />
          ) : (
            <KeyboardArrowDownRounded sx={{ fontSize: 18 }} />
          )}
        </IconButton>
      </Stack>

      {!expanded && isRunning && !hasPendingPermissions && latestPermissions.length > 0 ? (
        <Stack spacing={0} sx={{ px: 1.5, pb: 1.25 }}>
          {latestPermissions.map((permission, index) => {
            const primaryValue = getPermissionPrimaryValue(permission);
            const isLast = index === latestPermissions.length - 1;
            const itemColor = getPermissionStatusColor(permission.status, theme);
            const itemTextColor = getPermissionStatusTextColor(permission.status, theme);

            return (
              <Stack
                key={permission.requestId}
                direction="row"
                spacing={1}
                alignItems="center"
                sx={{
                  py: 0.8,
                  borderTop: index === 0 ? `1px solid ${alpha(chipColor, 0.1)}` : "none",
                  borderBottom: isLast ? "none" : `1px solid ${alpha("#000", 0.045)}`,
                }}
              >
                <Box
                  sx={{
                    width: 24,
                    height: 24,
                    borderRadius: "50%",
                    display: "grid",
                    placeItems: "center",
                    flex: "0 0 auto",
                    color: itemColor,
                    bgcolor: alpha(itemColor, 0.08),
                  }}
                >
                  <PermissionIcon permission={permission} />
                </Box>
                <Box sx={{ minWidth: 0, flex: 1 }}>
                  <Typography variant="caption" color="text.primary" fontWeight={750} noWrap sx={{ display: "block" }}>
                    {getPermissionTitle(permission)}
                  </Typography>
                  <Typography
                    variant="caption"
                    color="text.secondary"
                    noWrap
                    sx={{
                      display: "block",
                      fontFamily: permission.permission === "bash" ? "monospace" : undefined,
                    }}
                  >
                    {truncateText(primaryValue, 72)}
                  </Typography>
                </Box>
                <Chip
                  size="small"
                  label={getPermissionStatusLabel(permission.status)}
                  sx={{
                    height: 22,
                    borderRadius: "11px",
                    fontSize: "0.68rem",
                    fontWeight: 800,
                    color: itemTextColor,
                    bgcolor: alpha(itemColor, 0.08),
                    "& .MuiChip-label": { px: 0.85 },
                  }}
                />
              </Stack>
            );
          })}
        </Stack>
      ) : null}

      <AnimatePresence initial={false}>
        {!expanded && isRunning && hasPendingPermissions ? (
          <motion.div
            key="pending-permissions"
            initial={{ opacity: 0, y: -10, height: 0 }}
            animate={{ opacity: 1, y: 0, height: "auto" }}
            exit={{ opacity: 0, y: -8, height: 0 }}
            transition={{ duration: 0.2, ease: "easeOut" }}
            style={{ overflow: "hidden" }}
          >
            <Stack spacing={1} sx={{ px: 1.25, pb: 1.25 }}>
              {pendingPermissions.map((permission) => (
                <PermissionRequestCard
                  key={permission.requestId}
                  permission={permission}
                  onReply={onReply}
                />
              ))}
            </Stack>
          </motion.div>
        ) : null}
      </AnimatePresence>

      <Collapse in={expanded} timeout="auto" unmountOnExit>
        <Stack spacing={1} sx={{ px: 1.25, pb: 1.25 }}>
          {permissions.map((permission) => (
            <PermissionRequestCard
              key={permission.requestId}
              permission={permission}
              onReply={onReply}
            />
          ))}
        </Stack>
      </Collapse>
    </Box>
  );
};

const getQuestionStatusLabel = (
  status: NonNullable<Message["questions"]>[number]["status"],
) => {
  if (status === "answered") return "已回答";
  if (status === "rejected") return "已跳过";
  if (status === "error") return "提交失败";
  if (status === "submitting") return "提交中";
  return "等待回答";
};

const getQuestionStatusColor = (
  status: NonNullable<Message["questions"]>[number]["status"],
  theme: Theme,
) => {
  if (status === "answered") return theme.palette.success.main;
  if (status === "rejected") return theme.palette.text.secondary;
  if (status === "error") return theme.palette.error.main;
  return "#0288d1";
};

const QuestionRequestCard = ({
  questionRequest,
  onReply,
  onReject,
}: {
  questionRequest: NonNullable<Message["questions"]>[number];
  onReply: (requestId: string, answers: string[][]) => void;
  onReject: (requestId: string) => void;
}) => {
  const theme = useTheme();
  const isEditable =
    questionRequest.status === "pending" || questionRequest.status === "error";
  const isSubmitting = questionRequest.status === "submitting";
  const statusColor = getQuestionStatusColor(questionRequest.status, theme);
  const [selected, setSelected] = React.useState<Record<number, string[]>>({});
  const [customSelected, setCustomSelected] = React.useState<Record<number, boolean>>({});
  const [custom, setCustom] = React.useState<Record<number, string>>({});

  const answers = React.useMemo(
    () =>
      questionRequest.questions.map((question, index) => {
        const selectedAnswers = selected[index] ?? [];
        const isCustomSelected =
          customSelected[index] === true ||
          (question.custom !== false && question.options.length === 0);
        const customAnswer = custom[index]?.trim();
        return isCustomSelected && customAnswer
          ? [...selectedAnswers, customAnswer]
          : selectedAnswers;
      }),
    [custom, customSelected, questionRequest.questions, selected],
  );

  const canSubmit =
    isEditable &&
    questionRequest.questions.length > 0 &&
    questionRequest.questions.every((_, index) => {
      const answer = answers[index] ?? [];
      return answer.some((item) => item.trim().length > 0);
    });

  const answerSummary = (questionRequest.answers ?? [])
    .map((answer) => answer.join("、"))
    .filter(Boolean)
    .join("；");

  return (
    <Box
      sx={{
        borderRadius: 3,
        overflow: "hidden",
        border: `1px solid ${alpha("#fff", 0.72)}`,
        bgcolor: alpha("#fff", 0.52),
        boxShadow: `0 8px 24px ${alpha("#000", 0.05)}`,
        backdropFilter: "blur(20px)",
        position: "relative",
        "&::before": {
          content: '""',
          position: "absolute",
          inset: "10px auto 10px 0",
          width: 3,
          borderRadius: "0 999px 999px 0",
          bgcolor: statusColor,
        },
      }}
    >
      <Stack
        direction="row"
        spacing={1}
        alignItems="center"
        sx={{
          px: 1.5,
          py: 1.25,
          pl: 1.75,
          borderBottom: `1px solid ${alpha("#000", 0.05)}`,
        }}
      >
        <Box
          sx={{
            width: 32,
            height: 32,
            borderRadius: "50%",
            display: "grid",
            placeItems: "center",
            flex: "0 0 auto",
            color: statusColor,
            bgcolor: alpha(statusColor, 0.1),
            border: `1px solid ${alpha(statusColor, 0.16)}`,
          }}
        >
          <HelpOutlineRounded sx={{ fontSize: 21 }} />
        </Box>
        <Box sx={{ minWidth: 0, flex: 1 }}>
          <Typography variant="subtitle2" fontWeight={800} noWrap sx={{ lineHeight: 1.25 }}>
            需要补充信息
          </Typography>
        </Box>
        <Chip
          size="small"
          label={getQuestionStatusLabel(questionRequest.status)}
          sx={{
            height: 24,
            fontSize: "0.7rem",
            fontWeight: 800,
            borderRadius: "12px",
            bgcolor: alpha(statusColor, 0.12),
            color: statusColor,
            "& .MuiChip-label": { px: 1 },
          }}
        />
      </Stack>

      <Stack spacing={1.3} sx={{ px: 1.5, py: 1.35, pl: 1.75 }}>
        {questionRequest.questions.map((question, index) => {
          const selectedAnswers = selected[index] ?? [];
          const isCustomEnabled = question.custom !== false;
          const isCustomSelected =
            customSelected[index] === true ||
            (isCustomEnabled && question.options.length === 0);
          const setQuestionAnswers = (nextAnswers: string[]) => {
            setSelected((current) => ({
              ...current,
              [index]: nextAnswers,
            }));
          };
          const setQuestionCustomSelected = (checked: boolean) => {
            setCustomSelected((current) => ({
              ...current,
              [index]: checked,
            }));
          };

          return (
            <Box
              key={`${question.header}-${index}`}
              sx={{
                px: 1.25,
                py: 1,
                borderRadius: 2.5,
                bgcolor: alpha("#000", 0.025),
                border: `1px solid ${alpha("#000", 0.045)}`,
              }}
            >
              <Typography variant="caption" color="text.secondary" fontWeight={800}>
                {question.header || `问题 ${index + 1}`}
              </Typography>
              <Typography
                variant="body2"
                color="text.primary"
                sx={{ mt: 0.35, lineHeight: 1.55, wordBreak: "break-word" }}
              >
                {question.question}
              </Typography>

              {question.options.length ? (
                <Stack spacing={0.75} sx={{ mt: 1 }}>
                  {question.options.map((option) => {
                    const checked = selectedAnswers.includes(option.label);
                    if (question.multiple) {
                      return (
                        <FormControlLabel
                          key={option.label}
                          disabled={!isEditable || isSubmitting}
                          control={
                            <Checkbox
                              size="small"
                              checked={checked}
                              onChange={(event) => {
                                if (event.target.checked) {
                                  setQuestionAnswers([...selectedAnswers, option.label]);
                                } else {
                                  setQuestionAnswers(
                                    selectedAnswers.filter((item) => item !== option.label),
                                  );
                                }
                              }}
                            />
                          }
                          label={
                            <Box>
                              <Typography variant="body2" fontWeight={750}>
                                {option.label}
                              </Typography>
                              {option.description ? (
                                <Typography variant="caption" color="text.secondary">
                                  {option.description}
                                </Typography>
                              ) : null}
                            </Box>
                          }
                          sx={{ alignItems: "flex-start", m: 0 }}
                        />
                      );
                    }
                    return (
                      <Button
                        key={option.label}
                        size="small"
                        variant={checked ? "contained" : "outlined"}
                        disabled={!isEditable || isSubmitting}
                        onClick={() => {
                          setQuestionAnswers([option.label]);
                          setQuestionCustomSelected(false);
                        }}
                        startIcon={
                          checked ? (
                            <CheckCircleRounded fontSize="small" />
                          ) : (
                            <RadioButtonUncheckedRounded fontSize="small" />
                          )
                        }
                        sx={{
                          justifyContent: "flex-start",
                          minHeight: 38,
                          borderRadius: 2,
                          textTransform: "none",
                          fontWeight: 800,
                          bgcolor: checked ? "#0288d1" : alpha("#fff", 0.45),
                          borderColor: checked ? "#0288d1" : alpha("#0288d1", 0.22),
                          "&:hover": {
                            bgcolor: checked ? "#0277bd" : alpha("#0288d1", 0.08),
                          },
                        }}
                      >
                        <Box sx={{ textAlign: "left", minWidth: 0 }}>
                          <Typography variant="body2" fontWeight={800}>
                            {option.label}
                          </Typography>
                          {option.description ? (
                            <Typography
                              variant="caption"
                              sx={{ display: "block", opacity: checked ? 0.86 : 0.72 }}
                            >
                              {option.description}
                            </Typography>
                          ) : null}
                        </Box>
                      </Button>
                    );
                  })}
                  {isCustomEnabled ? (
                    question.multiple ? (
                      <FormControlLabel
                        disabled={!isEditable || isSubmitting}
                        control={
                          <Checkbox
                            size="small"
                            checked={isCustomSelected}
                            onChange={(event) =>
                              setQuestionCustomSelected(event.target.checked)
                            }
                            sx={{
                              p: 0.5,
                              color: alpha("#0288d1", 0.55),
                              "&.Mui-checked": { color: "#0288d1" },
                            }}
                          />
                        }
                        label={
                          <Stack direction="row" spacing={0.75} alignItems="center">
                            <EditNoteRounded sx={{ fontSize: 18, color: "#0288d1" }} />
                            <Typography variant="body2" fontWeight={800}>
                              自定义回答
                            </Typography>
                          </Stack>
                        }
                        sx={{
                          alignItems: "center",
                          minHeight: 38,
                          m: 0,
                          px: 0.75,
                          py: 0.25,
                          borderRadius: 2,
                          border: `1px solid ${
                            isCustomSelected ? "#0288d1" : alpha("#0288d1", 0.18)
                          }`,
                          bgcolor: isCustomSelected
                            ? alpha("#0288d1", 0.1)
                            : alpha("#fff", 0.45),
                          transition: "background-color 0.18s ease, border-color 0.18s ease",
                          "&:hover": {
                            bgcolor: isCustomSelected
                              ? alpha("#0288d1", 0.13)
                              : alpha("#0288d1", 0.07),
                          },
                          "& .MuiFormControlLabel-label": {
                            color: isCustomSelected ? "#0277bd" : "text.primary",
                          },
                        }}
                      />
                    ) : (
                      <Button
                        size="small"
                        variant={isCustomSelected ? "contained" : "outlined"}
                        disabled={!isEditable || isSubmitting}
                        onClick={() => {
                          setQuestionAnswers([]);
                          setQuestionCustomSelected(true);
                        }}
                        startIcon={
                          isCustomSelected ? (
                            <CheckCircleRounded fontSize="small" />
                          ) : (
                            <EditNoteRounded fontSize="small" />
                          )
                        }
                        sx={{
                          justifyContent: "flex-start",
                          minHeight: 38,
                          borderRadius: 2,
                          textTransform: "none",
                          fontWeight: 800,
                          bgcolor: isCustomSelected ? "#0288d1" : alpha("#fff", 0.45),
                          borderColor: isCustomSelected
                            ? "#0288d1"
                            : alpha("#0288d1", 0.22),
                          "&:hover": {
                            bgcolor: isCustomSelected
                              ? "#0277bd"
                              : alpha("#0288d1", 0.08),
                          },
                        }}
                      >
                        <Box sx={{ textAlign: "left", minWidth: 0 }}>
                          <Typography variant="body2" fontWeight={800}>
                            自定义回答
                          </Typography>
                        </Box>
                      </Button>
                    )
                  ) : null}
                </Stack>
              ) : null}

              <Collapse in={isCustomEnabled && isCustomSelected} timeout="auto" unmountOnExit>
                <Box
                  sx={{
                    mt: 0.85,
                    px: 1.15,
                    py: 0.85,
                    borderRadius: 2.5,
                    bgcolor: alpha("#fff", 0.62),
                    border: `1px solid ${alpha("#fff", 0.82)}`,
                    boxShadow: `0 8px 22px ${alpha("#000", 0.045)}, 0 0 0 1px ${alpha("#0288d1", 0.05)} inset`,
                    backdropFilter: "blur(18px)",
                  }}
                >
                  <TextField
                    multiline
                    minRows={2}
                    maxRows={5}
                    fullWidth
                    variant="standard"
                    disabled={!isEditable || isSubmitting}
                    value={custom[index] ?? ""}
                    onChange={(event) =>
                      setCustom((current) => ({
                        ...current,
                        [index]: event.target.value,
                      }))
                    }
                    placeholder="输入自定义回答"
                    InputProps={{
                      disableUnderline: true,
                      sx: {
                        alignItems: "flex-start",
                        fontSize: "0.88rem",
                        lineHeight: 1.55,
                        fontWeight: 500,
                        color: "text.primary",
                        "& textarea::placeholder": {
                          color: alpha(theme.palette.text.primary, 0.38),
                          opacity: 1,
                        },
                      },
                    }}
                  />
                </Box>
              </Collapse>
            </Box>
          );
        })}

        {questionRequest.status === "answered" ? (
          <Typography
            variant="caption"
            color="success.main"
            sx={{
              display: "block",
              px: 1.25,
              py: 0.75,
              borderRadius: 2,
              bgcolor: alpha(theme.palette.success.main, 0.07),
              wordBreak: "break-word",
            }}
          >
            已回答{answerSummary ? `：${answerSummary}` : ""}
          </Typography>
        ) : null}

        {questionRequest.status === "rejected" ? (
          <Typography
            variant="caption"
            color="text.secondary"
            sx={{
              display: "block",
              px: 1.25,
              py: 0.75,
              borderRadius: 2,
              bgcolor: alpha("#000", 0.035),
            }}
          >
            已跳过
          </Typography>
        ) : null}

        {questionRequest.error ? (
          <Typography
            variant="caption"
            color="error.main"
            sx={{
              display: "block",
              px: 1.25,
              py: 0.75,
              borderRadius: 2,
              bgcolor: alpha(theme.palette.error.main, 0.06),
              wordBreak: "break-word",
            }}
          >
            {questionRequest.error}
          </Typography>
        ) : null}
      </Stack>

      {isEditable || isSubmitting ? (
        <Stack
          direction="row"
          spacing={1}
          flexWrap="wrap"
          useFlexGap
          sx={{ px: 1.5, pb: 1.35, pl: 1.75 }}
        >
          <Button
            size="small"
            variant="outlined"
            disabled={isSubmitting}
            onClick={() => onReject(questionRequest.requestId)}
            sx={{
              height: 34,
              borderRadius: "17px",
              px: 1.5,
              fontWeight: 800,
              fontSize: "0.78rem",
              textTransform: "none",
              color: "text.secondary",
              borderColor: alpha(theme.palette.text.secondary, 0.22),
              bgcolor: alpha("#fff", 0.45),
            }}
          >
            跳过
          </Button>
          <Button
            size="small"
            variant="contained"
            disableElevation
            disabled={!canSubmit || isSubmitting}
            onClick={() => onReply(questionRequest.requestId, answers)}
            startIcon={
              isSubmitting ? (
                <CircularProgress size={14} color="inherit" />
              ) : (
                <CheckCircleRounded fontSize="small" />
              )
            }
            sx={{
              minWidth: 104,
              height: 34,
              borderRadius: "17px",
              bgcolor: "#0288d1",
              fontWeight: 800,
              fontSize: "0.78rem",
              textTransform: "none",
              boxShadow: `0 4px 12px ${alpha("#0288d1", 0.24)}`,
              "&:hover": {
                bgcolor: "#0277bd",
                boxShadow: `0 6px 16px ${alpha("#0288d1", 0.28)}`,
              },
            }}
          >
            提交回答
          </Button>
        </Stack>
      ) : null}
    </Box>
  );
};

const QuestionRequestGroup = ({
  questions,
  onReply,
  onReject,
}: {
  questions: NonNullable<Message["questions"]>;
  onReply: (requestId: string, answers: string[][]) => void;
  onReject: (requestId: string) => void;
}) => (
  <Stack spacing={1}>
    {questions.map((question) => (
      <QuestionRequestCard
        key={question.requestId}
        questionRequest={question}
        onReply={onReply}
        onReject={onReject}
      />
    ))}
  </Stack>
);

const TodoPlanCard = ({
  todoUpdate,
}: {
  todoUpdate: NonNullable<Message["todos"]>;
}) => {
  const theme = useTheme();
  const total = todoUpdate.todos.length;
  const completed = todoUpdate.todos.filter((todo) => todo.status === "completed").length;
  const running = todoUpdate.todos.find((todo) => todo.status === "in_progress");
  const cancelled = todoUpdate.todos.filter((todo) => todo.status === "cancelled").length;
  const pending = todoUpdate.todos.filter((todo) => todo.status === "pending").length;
  const progress = total > 0 ? Math.round((completed / total) * 100) : 0;
  const isAborted = cancelled > 0 && completed + cancelled === total;
  const canCollapse = total > 4;
  const [expanded, setExpanded] = React.useState(!canCollapse && !isAborted);
  const pinnedTodos = canCollapse ? todoUpdate.todos.slice(0, 4) : todoUpdate.todos;
  const collapsibleTodos = canCollapse ? todoUpdate.todos.slice(4) : [];
  const hiddenCount = expanded ? 0 : collapsibleTodos.length;
  const latestUpdatedAt = Math.max(
    todoUpdate.createdAt,
    ...todoUpdate.todos
      .map((todo) => todo.updatedAt ?? todo.createdAt ?? 0)
      .filter((value) => value > 0),
  );
  const updatedAtLabel =
    latestUpdatedAt > 0
      ? new Intl.DateTimeFormat("zh-CN", {
          hour: "2-digit",
          minute: "2-digit",
        }).format(new Date(latestUpdatedAt))
      : undefined;

  const getTodoVisual = (status: NonNullable<Message["todos"]>["todos"][number]["status"]) => {
    if (status === "completed") {
      return { icon: <CheckCircleRounded sx={{ fontSize: 17 }} />, color: theme.palette.success.main, label: "完成" };
    }
    if (status === "in_progress") {
      return { icon: <CircularProgress size={15} thickness={5} />, color: "#0288d1", label: "进行中" };
    }
    if (status === "cancelled") {
      return { icon: <BlockRounded sx={{ fontSize: 17 }} />, color: theme.palette.text.disabled, label: "中止" };
    }
    return { icon: <RadioButtonUncheckedRounded sx={{ fontSize: 17 }} />, color: theme.palette.text.secondary, label: "待办" };
  };

  const getPriorityLabel = (priority: NonNullable<Message["todos"]>["todos"][number]["priority"]) => {
    if (priority === "high") return { label: "高优先级", color: "#8a5a00" };
    if (priority === "medium") return { label: "中优先级", color: "#9a6a16" };
    if (priority === "low") return { label: "低优先级", color: "#8d7960" };
    return undefined;
  };

  const statusSummary = isAborted
    ? `${completed} 完成 / ${cancelled} 中止`
    : [
        completed ? `${completed} 完成` : null,
        running ? "1 进行中" : null,
        pending ? `${pending} 待办` : null,
        cancelled ? `${cancelled} 中止` : null,
      ].filter(Boolean).join(" / ") || "等待任务";
  const renderTodoRow = (
    todo: NonNullable<Message["todos"]>["todos"][number],
    index: number,
  ) => {
    const visual = getTodoVisual(todo.status);
    const priority = getPriorityLabel(todo.priority);
    return (
      <Stack
        key={`${todo.id}-${index}`}
        direction="row"
        alignItems="flex-start"
        spacing={1}
        sx={{
          py: 0.8,
          borderTop: `1px solid ${alpha("#00838f", 0.08)}`,
          color: todo.status === "cancelled" ? "text.disabled" : "text.primary",
        }}
      >
        <Box
          sx={{
            width: 24,
            height: 24,
            borderRadius: 1.25,
            display: "grid",
            placeItems: "center",
            flex: "0 0 auto",
            color: visual.color,
            bgcolor: alpha(visual.color, 0.08),
            mt: 0.1,
          }}
        >
          {visual.icon}
        </Box>
        <Box sx={{ minWidth: 0, flex: 1 }}>
          <Typography
            variant="body2"
            sx={{
              minWidth: 0,
              wordBreak: "break-word",
              lineHeight: 1.45,
              textDecoration: todo.status === "cancelled" ? "line-through" : undefined,
            }}
          >
            {todo.content}
          </Typography>
        </Box>
        <Stack direction="row" spacing={0.5} sx={{ flex: "0 0 auto" }}>
          {priority ? (
            <Chip
              size="small"
              label={priority.label}
              sx={{
                height: 22,
                borderRadius: "11px",
                fontSize: "0.66rem",
                fontWeight: 800,
                color: priority.color,
                bgcolor: alpha(priority.color, 0.045),
                border: `1px solid ${alpha(priority.color, 0.16)}`,
                "& .MuiChip-label": { px: 0.75 },
              }}
            />
          ) : null}
          <Chip
            size="small"
            label={visual.label}
            sx={{
              height: 22,
              borderRadius: "11px",
              fontSize: "0.66rem",
              fontWeight: 800,
              color: visual.color,
              bgcolor: alpha(visual.color, 0.08),
              "& .MuiChip-label": { px: 0.75 },
            }}
          />
        </Stack>
      </Stack>
    );
  };

  if (total === 0) {
    return null;
  }

  return (
    <Box
      sx={{
        borderRadius: 2,
        overflow: "hidden",
        border: `1px solid ${alpha("#00838f", 0.16)}`,
        bgcolor: alpha("#f8fbfc", 0.82),
      }}
    >
      <Stack
        spacing={1}
        role="button"
        tabIndex={0}
        onClick={() => {
          if (canCollapse) {
            setExpanded((value) => !value);
          }
        }}
        onKeyDown={(event) => {
          if (canCollapse && (event.key === "Enter" || event.key === " ")) {
            event.preventDefault();
            setExpanded((value) => !value);
          }
        }}
        sx={{
          px: 1.4,
          py: 1.15,
          cursor: canCollapse ? "pointer" : "default",
          transition: "background-color 0.2s ease",
          "&:hover": canCollapse ? { bgcolor: alpha("#00838f", 0.035) } : undefined,
        }}
      >
        <Stack direction="row" alignItems="center" spacing={1}>
          <Box
            sx={{
              width: 28,
              height: 28,
              borderRadius: 1.5,
              display: "grid",
              placeItems: "center",
              flex: "0 0 auto",
              color: "#00838f",
              bgcolor: alpha("#00838f", 0.1),
              border: `1px solid ${alpha("#00838f", 0.14)}`,
            }}
          >
            <AssignmentTurnedInRounded sx={{ fontSize: 18 }} />
          </Box>
          <Box sx={{ minWidth: 0, flex: 1 }}>
            <Stack direction="row" alignItems="center" spacing={0.75}>
              <Typography variant="subtitle2" fontWeight={800} noWrap sx={{ lineHeight: 1.25 }}>
                会话任务
              </Typography>
              <Chip
                size="small"
                label={running ? "执行中" : isAborted ? "已中止" : completed === total ? "已完成" : "已同步"}
                sx={{
                  height: 20,
                  borderRadius: "10px",
                  fontSize: "0.66rem",
                  fontWeight: 800,
                  color: running ? "#0277bd" : isAborted ? "text.secondary" : "#00838f",
                  bgcolor: alpha(running ? "#0288d1" : isAborted ? "#64748b" : "#00838f", 0.08),
                  "& .MuiChip-label": { px: 0.75 },
                }}
              />
            </Stack>
            <Typography variant="caption" color="text.secondary">
              {statusSummary}{updatedAtLabel ? ` · ${updatedAtLabel} 更新` : ""}
            </Typography>
          </Box>
          {canCollapse ? (
            <IconButton
              size="small"
              aria-label={expanded ? "收起会话任务" : "展开会话任务"}
              sx={{
                width: 28,
                height: 28,
                color: "text.secondary",
                bgcolor: alpha("#000", 0.035),
                "&:hover": { bgcolor: alpha("#000", 0.07) },
              }}
            >
              {expanded ? (
                <KeyboardArrowUpRounded sx={{ fontSize: 18 }} />
              ) : (
                <KeyboardArrowDownRounded sx={{ fontSize: 18 }} />
              )}
            </IconButton>
          ) : null}
        </Stack>
        <Box
          sx={{
            height: 6,
            borderRadius: 999,
            overflow: "hidden",
            bgcolor: alpha("#00838f", 0.1),
          }}
        >
          <Box
            sx={{
              width: `${progress}%`,
              height: "100%",
              borderRadius: 999,
              bgcolor: isAborted ? theme.palette.text.disabled : "#00838f",
              transition: "width 0.25s ease",
            }}
          />
        </Box>
      </Stack>

      <Stack spacing={0} sx={{ px: 1.4, pb: 1.1 }}>
        {pinnedTodos.map((todo, index) => renderTodoRow(todo, index))}
        {canCollapse ? (
          <Collapse in={expanded} timeout={220} unmountOnExit={false}>
            <Stack spacing={0}>
              {collapsibleTodos.map((todo, index) =>
                renderTodoRow(todo, index + pinnedTodos.length),
              )}
            </Stack>
          </Collapse>
        ) : null}
        {hiddenCount > 0 ? (
          <Typography
            variant="caption"
            color="text.secondary"
            sx={{
              pt: 0.8,
              borderTop: `1px solid ${alpha("#00838f", 0.08)}`,
            }}
          >
            还有 {hiddenCount} 项，展开查看全部
          </Typography>
        ) : null}
      </Stack>
    </Box>
  );
};

export const AgentTurn = React.memo(
  ({
    message,
    messageSpeechState,
    onSpeak,
    onPause,
    onResume,
    onStopSpeech,
    isTtsSupported,
    onRegenerate,
    onCreateBranch,
    onReplyPermission,
    onReplyQuestion,
    onRejectQuestion,
  }: AgentTurnProps) => {
    const theme = useTheme();
    const isUser = message.role === "user";
    const isErrorMessage = Boolean(message.isError);
    const [isHovered, setIsHovered] = React.useState(false);
    const isProgressComplete = message.progress?.some(
      (item) => item.phase === "complete" && item.status === "completed",
    ) ?? false;
    const isProgressRunning = !isErrorMessage && !isProgressComplete && (
      message.progress?.some((item) => item.status === "running") ?? false
    );

    const parsedAssistantSections = useMemo(
      () =>
        !isUser && !isErrorMessage
          ? parseAssistantMessageSections(message.content)
          : null,
      [isErrorMessage, isUser, message.content],
    );
    const answerContent = parsedAssistantSections?.answer ?? message.content;
    const contentSegments: ContentSegment[] = useMemo(
      () =>
        !isUser && !isErrorMessage
          ? parseContentWithToolCalls(answerContent).segments
          : [{ type: "text", content: answerContent }],
      [answerContent, isErrorMessage, isUser],
    );
    
    if (isUser) {
      return (
        <motion.div
          initial={{ opacity: 0, y: 12, scale: 0.98 }}
          animate={{ opacity: 1, y: 0, scale: 1 }}
          exit={{ opacity: 0, y: 8 }}
          transition={{ type: "spring", stiffness: 350, damping: 25 }}
          style={{ alignSelf: "flex-end", maxWidth: "86%", position: "relative" }}
          onMouseEnter={() => setIsHovered(true)}
          onMouseLeave={() => setIsHovered(false)}
        >
          <Paper
            elevation={4}
            sx={{
              p: 2,
              borderRadius: 5,
              borderBottomRightRadius: 2,
              color: "#fff",
              background: `linear-gradient(135deg, #0288d1, #00acc1)`,
              boxShadow: `0 8px 24px -8px ${alpha("#00acc1", 0.5)}, inset 0 2px 4px ${alpha("#fff", 0.2)}`,
              backdropFilter: "blur(10px)",
              "--chat-md-text": alpha("#fff", 0.96),
              "--chat-md-heading": "#fff",
              "--chat-md-link": "#e0f7fa",
              "--chat-md-link-hover": "#fff",
              "--chat-md-inline-code-bg": "rgba(255,255,255,0.15)",
              "--chat-md-inline-code-border": alpha("#fff", 0.1),
              "--chat-md-inline-code-text": "#fff",
              "--chat-md-pre-bg": "rgba(0, 0, 0, 0.25)",
              "--chat-md-pre-border": alpha("#fff", 0.1),
              "--chat-md-pre-text": "#F8FAFC",
              "--chat-md-quote-border": alpha("#fff", 0.4),
              "--chat-md-quote-bg": alpha("#fff", 0.05),
              "--chat-md-quote-text": alpha("#fff", 0.8),
            }}
          >
            <MarkdownBlock>{message.content}</MarkdownBlock>
          </Paper>
        </motion.div>
      );
    }

    return (
      <motion.div
        initial={{ opacity: 0, y: 14 }}
        animate={{ opacity: 1, y: 0 }}
        exit={{ opacity: 0, y: 8 }}
        transition={{ type: "spring", stiffness: 320, damping: 26 }}
        style={{ width: "100%", position: "relative" }}
        onMouseEnter={() => setIsHovered(true)}
        onMouseLeave={() => setIsHovered(false)}
      >
        <Stack direction="row" spacing={1.5} alignItems="flex-start">
          <Avatar
            sx={{
              width: 34,
              height: 34,
              background: alpha("#ffffff", 0.9),
              boxShadow: `0 4px 12px ${alpha("#00acc1", 0.25)}`,
              border: `1.5px solid ${alpha("#fff", 0.8)}`,
              color: "#00acc1",
              mt: 0.25,
              p: 0.5,
            }}
          >
            <Image
              src="/ai-agent.svg"
              alt="TJWater Agent"
              width={18}
              height={18}
              style={{ objectFit: "contain" }}
            />
          </Avatar>

          <Paper
            elevation={0}
            sx={{
              flex: 1,
              minWidth: 0,
              p: 2,
              borderRadius: 5,
              bgcolor: alpha("#ffffff", 0.65),
              border: `1px solid ${alpha("#fff", 0.8)}`,
              boxShadow: `0 10px 30px -10px ${alpha(theme.palette.common.black, 0.08)}`,
              backdropFilter: "blur(20px)",
              position: "relative",
              "--chat-md-text": "text.primary",
              "--chat-md-heading": "text.primary",
              "--chat-md-link": "#00838f",
              "--chat-md-link-hover": "#00acc1",
              "--chat-md-inline-code-bg": alpha("#00acc1", 0.08),
              "--chat-md-inline-code-border": alpha("#00acc1", 0.15),
              "--chat-md-inline-code-text": "#006064",
              "--chat-md-pre-bg": "#1e293b",
              "--chat-md-pre-border": "#475569",
              "--chat-md-pre-text": "#f1f5f9",
              "--chat-md-quote-border": "#00acc1",
              "--chat-md-quote-bg": alpha("#00acc1", 0.04),
              "--chat-md-quote-text": "text.secondary",
            }}
          >
            <Stack spacing={1.5}>
              {message.progress?.length ? (
                <AgentProgressTimeline progress={message.progress} isAborted={isErrorMessage} />
              ) : null}

              {message.permissions?.length ? (
                <PermissionRequestGroup
                  permissions={message.permissions}
                  isRunning={isProgressRunning}
                  onReply={onReplyPermission}
                />
              ) : null}

              {message.questions?.length ? (
                <QuestionRequestGroup
                  questions={message.questions}
                  onReply={onReplyQuestion}
                  onReject={onRejectQuestion}
                />
              ) : null}

              {message.todos ? (
                <TodoPlanCard todoUpdate={message.todos} />
              ) : null}

              <Box
                sx={{
                  p: 1.5,
                  borderRadius: 4,
                  bgcolor: alpha("#fff", 0.4),
                  border: `1px solid ${alpha("#fff", 0.6)}`,
                }}
              >
                <Stack spacing={1.2}>
                  <Typography variant="caption" color="text.secondary" fontWeight={800} sx={{ letterSpacing: 0.5 }}>
                    分析结果
                  </Typography>
                  {contentSegments.map((segment, segIdx) => {
                    if (segment.type === "text") {
                      const text = segment.content.trim();
                      if (!text && contentSegments.length > 1) return null;
                      return <MarkdownBlock key={segIdx}>{text || "..."}</MarkdownBlock>;
                    }
                    if (segment.type === "tool_call") {
                      if (
                        segment.toolCall.tool === "chart" ||
                        segment.toolCall.tool === "show_chart"
                      ) {
                        const p = segment.toolCall.params;
                        return (
                          <ChatInlineChart
                            key={segment.toolCall.id}
                            title={(p.title as string) ?? undefined}
                            chart_type={
                              (p.chart_type as "line" | "bar" | "pie") ?? "line"
                            }
                            x_data={(p.x_data as string[]) ?? []}
                            series={(p.series as ChatChartSeries[]) ?? []}
                            x_axis_name={(p.x_axis_name as string) ?? undefined}
                            y_axis_name={(p.y_axis_name as string) ?? undefined}
                          />
                        );
                      }
                      return (
                        <ChatToolCallBlock
                          key={segment.toolCall.id}
                          toolCall={segment.toolCall}
                        />
                      );
                    }
                    if (segment.type === "tool_call_pending") {
                      return (
                        <Typography key="tool-pending" variant="caption" color="text.secondary">
                          正在准备工具调用...
                        </Typography>
                      );
                    }
                    return null;
                  })}
                </Stack>
              </Box>
            </Stack>

            <AnimatePresence>
              {isHovered && (
                <motion.div
                  initial={{ opacity: 0, scale: 0.9, y: 5 }}
                  animate={{ opacity: 1, scale: 1, y: 0 }}
                  exit={{ opacity: 0, scale: 0.9, y: 5 }}
                  transition={{ duration: 0.15 }}
                  style={{ position: "absolute", top: -14, right: 12, zIndex: 10 }}
                >
                  <Paper
                    elevation={4}
                    sx={{
                      display: "flex",
                      gap: 0.5,
                      p: 0.5,
                      borderRadius: "16px",
                      bgcolor: alpha("#fff", 0.8),
                      backdropFilter: "blur(16px)",
                      border: `1px solid ${alpha("#fff", 0.9)}`,
                      boxShadow: `0 4px 12px ${alpha("#000", 0.08)}`,
                    }}
                  >
                    <Tooltip title="复制">
                      <IconButton 
                        size="small" 
                        aria-label="复制" 
                        onClick={() => {
                          navigator.clipboard.writeText(
                            normalizeClipboardText(message.content),
                          );
                          // Could add a toast here
                        }}
                        sx={{ width: 28, height: 28, color: "text.secondary", "&:hover": { color: "#00acc1", bgcolor: alpha("#00acc1", 0.1) } }}
                      >
                        <ContentCopyRounded sx={{ fontSize: 16 }} />
                      </IconButton>
                    </Tooltip>
                    <Tooltip title="重新生成">
                      <IconButton 
                        size="small" 
                        aria-label="重新生成" 
                        onClick={() => {
                          onRegenerate(message.id);
                        }}
                        sx={{ width: 28, height: 28, color: "text.secondary", "&:hover": { color: "#00acc1", bgcolor: alpha("#00acc1", 0.1) } }}
                      >
                        <RefreshRounded sx={{ fontSize: 16 }} />
                      </IconButton>
                    </Tooltip>
                    <Tooltip title="拆分为新会话">
                      <IconButton
                        size="small"
                        aria-label="拆分为新会话"
                        onClick={() => {
                          onCreateBranch(message.id);
                        }}
                        sx={{ width: 28, height: 28, color: "text.secondary", "&:hover": { color: "#00acc1", bgcolor: alpha("#00acc1", 0.1) } }}
                      >
                        <TbArrowsSplit2 size={16} />
                      </IconButton>
                    </Tooltip>
                  </Paper>
                </motion.div>
              )}
            </AnimatePresence>

          </Paper>
        </Stack>

        {!isErrorMessage && isTtsSupported ? (
          <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ mt: 0.5, ml: 6, mb: 1 }}>
            <Stack direction="row" spacing={0.5} sx={{ opacity: isHovered ? 1 : 0.4, transition: "opacity 0.2s" }}>
              {messageSpeechState === "idle" ? (
                <IconButton
                  size="small"
                  onClick={() => onSpeak(message.id, stripMarkdown(answerContent))}
                  aria-label="朗读消息"
                  sx={{ color: "text.secondary", opacity: 0.68, p: 0.5 }}
                >
                  <VolumeUpRounded sx={{ fontSize: 16 }} />
                </IconButton>
              ) : null}
              {messageSpeechState === "playing" ? (
                <>
                  <IconButton size="small" onClick={onPause} aria-label="暂停朗读" sx={{ color: "primary.main", p: 0.5 }}>
                    <PauseRounded sx={{ fontSize: 16 }} />
                  </IconButton>
                  <IconButton size="small" onClick={onStopSpeech} aria-label="停止朗读" sx={{ color: "error.main", p: 0.5 }}>
                    <StopRounded sx={{ fontSize: 16 }} />
                  </IconButton>
                </>
              ) : null}
              {messageSpeechState === "paused" ? (
                <>
                  <IconButton size="small" onClick={onResume} aria-label="继续朗读" sx={{ color: "primary.main", p: 0.5 }}>
                    <PlayArrowRounded sx={{ fontSize: 16 }} />
                  </IconButton>
                  <IconButton size="small" onClick={onStopSpeech} aria-label="停止朗读" sx={{ color: "error.main", p: 0.5 }}>
                    <StopRounded sx={{ fontSize: 16 }} />
                  </IconButton>
                </>
              ) : null}
            </Stack>
          </Stack>
        ) : null}
      </motion.div>
    );
  },
);

AgentTurn.displayName = "AgentTurn";
