"use client";

import Image from "next/image";
import React, { useMemo } from "react";
import { motion } from "framer-motion";
import {
  Avatar,
  Box,
  CircularProgress,
  Button,
  Grow,
  IconButton,
  Paper,
  Popper,
  Stack,
  Tooltip,
  Typography,
  alpha,
  useTheme,
} from "@mui/material";
import ContentCopyRounded from "@mui/icons-material/ContentCopyRounded";
import { TbArrowsSplit2 } from "react-icons/tb";
import type { PermissionReply } from "@/lib/chatStream";
import {
  parseAssistantMessageSections,
  parseContentWithToolCalls,
  type ContentSegment,
} from "./chatMessageSections";
import type {
  Message,
  SpeechState,
} from "./GlobalChatbox.types";
import { stripMarkdown } from "./globalChatboxUtils";
import { findSpeechSelectionStartOffset } from "./speechStartOptions";
import { AgentProgressTimeline } from "./AgentProgressTimeline";
import { ChartGenerationSkeleton, ChatInlineChart } from "./ChatInlineChart";
import { ChatToolCallBlock } from "./ChatToolCallBlock";
import { MarkdownBlock, normalizeClipboardText } from "./AgentMarkdownBlock";
import { PermissionRequestGroup } from "./AgentPermissionRequests";
import { QuestionRequestGroup } from "./AgentQuestionRequests";
import { TodoPlanCard } from "./AgentTodoPlanCard";
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";

const floatingActionSurfaceSx = {
  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)}`,
  overflow: "hidden",
} as const;

const floatingActionTransitionTimeout = { enter: 150, exit: 120 } as const;
const floatingIconButtonSx = {
  width: 28,
  height: 28,
  color: "text.secondary",
  "&:hover": {
    color: "#00acc1",
    bgcolor: alpha("#00acc1", 0.1),
  },
} as const;
const floatingSpeechButtonSx = {
  minHeight: 34,
  px: 1.25,
  color: "text.primary",
  fontSize: 13,
  fontWeight: 700,
  letterSpacing: 0,
  whiteSpace: "nowrap",
  borderRadius: "12px",
  "&:hover": {
    bgcolor: alpha("#00acc1", 0.1),
    color: "#00acc1",
  },
} as const;

type SpeechSelection = {
  startOffset: number;
  anchorRect: DOMRect;
};

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

const StreamingStatus = () => {
  const theme = useTheme();

  return (
    <Stack
      direction="row"
      spacing={0.75}
      alignItems="center"
      sx={{
        px: 1,
        py: 0.35,
        borderRadius: 999,
        bgcolor: alpha(theme.palette.primary.main, 0.07),
        color: "text.secondary",
      }}
    >
      <Stack direction="row" spacing={0.35} alignItems="center">
        {[0, 1, 2].map((index) => (
          <motion.span
            key={index}
            animate={{ opacity: [0.28, 0.86, 0.28] }}
            transition={{
              duration: 0.95,
              repeat: Infinity,
              delay: index * 0.14,
              ease: "easeInOut",
            }}
            style={{
              width: 4,
              height: 4,
              borderRadius: "50%",
              background: theme.palette.primary.main,
              display: "block",
            }}
          />
        ))}
      </Stack>
      <Typography variant="caption" color="text.secondary" fontWeight={700}>
        正在生成
      </Typography>
    </Stack>
  );
};

const StreamingMarkdownBlock = ({
  text,
  isStreaming,
  segmentKey,
}: {
  text: string;
  isStreaming: boolean;
  segmentKey: string;
}) => {
  const [streamTextState, setStreamTextState] = React.useState<{
    displayText: string;
    animatedTailLength: number;
  }>({
    displayText: text,
    animatedTailLength: 0,
  });

  React.useLayoutEffect(() => {
    setStreamTextState((current) => {
      if (current.displayText === text) {
        return current;
      }

      if (!isStreaming) {
        return {
          displayText: text,
          animatedTailLength: 0,
        };
      }

      if (current.displayText === text) {
        return current;
      }

      return {
        displayText: text,
        animatedTailLength:
          text.length > current.displayText.length &&
          text.startsWith(current.displayText)
            ? Math.min(48, text.length - current.displayText.length)
            : 0,
      };
    });
  }, [isStreaming, text]);

  return (
    <MarkdownBlock
      streamFadeKey={`${segmentKey}-${streamTextState.displayText.length}`}
      streamFadeLength={streamTextState.animatedTailLength}
    >
      {streamTextState.displayText}
    </MarkdownBlock>
  );
};

export const AgentTurn = React.memo(
  ({
    message,
    isStreaming,
    messageSpeechState,
    onSpeak,
    onPause,
    onResume,
    onStopSpeech,
    isTtsSupported,
    onCreateBranch,
    onReplyPermission,
    onReplyQuestion,
    onRejectQuestion,
  }: AgentTurnProps) => {
    const theme = useTheme();
    const isUser = message.role === "user";
    const isErrorMessage = Boolean(message.isError);
    const isStreamingAssistant = !isUser && !isErrorMessage && isStreaming;
    const [isHovered, setIsHovered] = React.useState(false);
    const answerContentRef = React.useRef<HTMLDivElement | null>(null);
    const [speechSelection, setSpeechSelection] = React.useState<SpeechSelection | null>(null);
    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 speechText = useMemo(
      () => stripMarkdown(answerContent),
      [answerContent],
    );
    const captureSpeechSelection = React.useCallback(() => {
      if (!isTtsSupported || isStreamingAssistant) {
        setSpeechSelection(null);
        return;
      }

      const selection = window.getSelection();
      const container = answerContentRef.current;
      if (!selection || selection.rangeCount === 0 || selection.isCollapsed || !container) {
        setSpeechSelection(null);
        return;
      }

      const range = selection.getRangeAt(0);
      if (!container.contains(range.commonAncestorContainer)) {
        setSpeechSelection(null);
        return;
      }

      const selectedText = selection.toString();
      const startOffset = findSpeechSelectionStartOffset(speechText, selectedText);
      if (startOffset === null) {
        setSpeechSelection(null);
        return;
      }

      const anchorRect = range.getBoundingClientRect();
      if (anchorRect.width === 0 && anchorRect.height === 0) {
        setSpeechSelection(null);
        return;
      }

      setSpeechSelection({ startOffset, anchorRect });
    }, [isStreamingAssistant, isTtsSupported, speechText]);
    const handleCaptureSpeechSelection = React.useCallback(() => {
      window.requestAnimationFrame(captureSpeechSelection);
    }, [captureSpeechSelection]);
    React.useEffect(() => {
      setSpeechSelection(null);
    }, [message.id, speechText]);
    React.useEffect(() => {
      if (!speechSelection) return;

      const closeSpeechSelection = () => setSpeechSelection(null);
      const handleSelectionChange = () => {
        if (window.getSelection()?.isCollapsed) {
          closeSpeechSelection();
        }
      };
      const handleKeyDown = (event: KeyboardEvent) => {
        if (event.key === "Escape") {
          closeSpeechSelection();
        }
      };

      document.addEventListener("selectionchange", handleSelectionChange);
      document.addEventListener("keydown", handleKeyDown);
      window.addEventListener("resize", closeSpeechSelection);
      window.addEventListener("scroll", closeSpeechSelection, true);

      return () => {
        document.removeEventListener("selectionchange", handleSelectionChange);
        document.removeEventListener("keydown", handleKeyDown);
        window.removeEventListener("resize", closeSpeechSelection);
        window.removeEventListener("scroll", closeSpeechSelection, true);
      };
    }, [speechSelection]);
    const handleSpeakMessage = () => {
      onSpeak(message.id, speechText);
    };
    const handleSpeakFromSelection = () => {
      if (!speechSelection) return;

      onSpeak(message.id, speechText, {
        startOffset: speechSelection.startOffset,
      });
      window.getSelection()?.removeAllRanges();
      setSpeechSelection(null);
    };
    const speechSelectionAnchor = React.useMemo(
      () => speechSelection
        ? {
            getBoundingClientRect: () => speechSelection.anchorRect,
          }
        : null,
      [speechSelection],
    );
    const isSpeechSelectionOpen = Boolean(speechSelection);
    const contentSegments: ContentSegment[] = useMemo(
      () =>
        !isUser && !isErrorMessage
          ? parseContentWithToolCalls(answerContent).segments
          : [{ type: "text", content: answerContent }],
      [answerContent, isErrorMessage, isUser],
    );
    const hasInlineChart = contentSegments.some(
      (segment) =>
        segment.type === "tool_call" &&
        (segment.toolCall.tool === "chart" ||
          segment.toolCall.tool === "show_chart"),
    );
    const visibleChartArtifacts = useMemo(
      () =>
        hasInlineChart
          ? []
          : (message.artifacts?.filter((artifact) => artifact.kind === "chart") ?? []),
      [hasInlineChart, message.artifacts],
    );
    
    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
                ref={answerContentRef}
                onPointerUp={handleCaptureSpeechSelection}
                onKeyUp={handleCaptureSpeechSelection}
                sx={{
                  p: 1.5,
                  borderRadius: 4,
                  bgcolor: alpha("#fff", 0.4),
                  border: `1px solid ${alpha("#fff", 0.6)}`,
                  position: "relative",
                }}
              >
                <Stack spacing={1.2}>
                  <Stack direction="row" alignItems="center" justifyContent="space-between" spacing={1}>
                    <Typography variant="caption" color="text.secondary" fontWeight={800} sx={{ letterSpacing: 0.5 }}>
                      分析结果
                    </Typography>
                    {isStreamingAssistant ? <StreamingStatus /> : null}
                  </Stack>
                  {contentSegments.map((segment, segIdx) => {
                    if (segment.type === "text") {
                      const text = segment.content.trim();
                      if (!text && contentSegments.length > 1) return null;
                      return (
                        <StreamingMarkdownBlock
                          key={segIdx}
                          text={text || "..."}
                          isStreaming={isStreamingAssistant}
                          segmentKey={`${message.id}-${segIdx}`}
                        />
                      );
                    }
                    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 ?? p.xData ?? p.labels ?? p.categories}
                            series={p.series}
                            x_axis_name={(p.x_axis_name as string) ?? undefined}
                            y_axis_name={(p.y_axis_name as string) ?? undefined}
                            isStreaming={isStreamingAssistant}
                          />
                        );
                      }
                      return (
                        <ChatToolCallBlock
                          key={segment.toolCall.id}
                          toolCall={segment.toolCall}
                        />
                      );
                    }
                    if (segment.type === "tool_call_pending") {
                      return (
                        <ChartGenerationSkeleton
                          key="tool-pending"
                          status={<StreamingStatus />}
                        />
                      );
                    }
                    return null;
                  })}
                </Stack>
              </Box>

              <Popper
                open={isSpeechSelectionOpen}
                anchorEl={speechSelectionAnchor}
                placement="top"
                transition
                modifiers={[
                  { name: "offset", options: { offset: [0, 8] } },
                  { name: "flip", enabled: true },
                  { name: "preventOverflow", options: { padding: 8 } },
                ]}
                sx={{ zIndex: theme.zIndex.tooltip }}
              >
                {({ TransitionProps, placement }) => (
                  <Grow
                    {...TransitionProps}
                    timeout={floatingActionTransitionTimeout}
                    style={{
                      transformOrigin: placement.startsWith("bottom")
                        ? "center top"
                        : "center bottom",
                    }}
                  >
                    <Paper elevation={4} sx={floatingActionSurfaceSx}>
                      <Button
                        size="small"
                        startIcon={<VolumeUpRounded sx={{ fontSize: 16 }} />}
                        onMouseDown={(event) => event.preventDefault()}
                        onClick={handleSpeakFromSelection}
                        sx={floatingSpeechButtonSx}
                      >
                        从这里开始朗读
                      </Button>
                    </Paper>
                  </Grow>
                )}
              </Popper>

              {visibleChartArtifacts.map((artifact) => (
                <ChatInlineChart
                  key={artifact.id}
                  title={(artifact.params.title as string) ?? artifact.title}
                  chart_type={
                    (artifact.params.chart_type as "line" | "bar" | "pie") ??
                    "line"
                  }
                  x_data={
                    artifact.params.x_data ??
                    artifact.params.xData ??
                    artifact.params.labels ??
                    artifact.params.categories
                  }
                  series={artifact.params.series}
                  x_axis_name={(artifact.params.x_axis_name as string) ?? undefined}
                  y_axis_name={(artifact.params.y_axis_name as string) ?? undefined}
                  isStreaming={isStreamingAssistant}
                />
              ))}
            </Stack>

            <Grow
              in={isHovered && !isStreaming}
              timeout={floatingActionTransitionTimeout}
              mountOnEnter
              unmountOnExit
              style={{ transformOrigin: "right bottom" }}
            >
              <Paper
                elevation={4}
                sx={{
                  ...floatingActionSurfaceSx,
                  position: "absolute",
                  top: -14,
                  right: 12,
                  zIndex: 10,
                }}
              >
                <Tooltip title="复制">
                  <IconButton
                    size="small"
                    aria-label="复制"
                    onClick={() => {
                      navigator.clipboard.writeText(
                        normalizeClipboardText(message.content),
                      );
                    }}
                    sx={floatingIconButtonSx}
                  >
                    <ContentCopyRounded sx={{ fontSize: 16 }} />
                  </IconButton>
                </Tooltip>
                <Tooltip title="拆分为新会话">
                  <IconButton
                    size="small"
                    aria-label="拆分为新会话"
                    onClick={() => onCreateBranch(message.id)}
                    sx={floatingIconButtonSx}
                  >
                    <TbArrowsSplit2 size={16} />
                  </IconButton>
                </Tooltip>
              </Paper>
            </Grow>

          </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={handleSpeakMessage}
                  aria-label="朗读消息"
                  sx={{ color: "text.secondary", opacity: 0.68, p: 0.5 }}
                >
                  <VolumeUpRounded sx={{ fontSize: 16 }} />
                </IconButton>
              ) : null}
              {messageSpeechState === "loading" ? (
                <>
                  <IconButton
                    size="small"
                    disabled
                    aria-label="正在生成语音"
                    sx={{ color: "primary.main", p: 0.5 }}
                  >
                    <CircularProgress size={16} thickness={5} />
                  </IconButton>
                  <IconButton size="small" onClick={onStopSpeech} aria-label="停止朗读" sx={{ color: "error.main", p: 0.5 }}>
                    <StopRounded 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";
