import { memo, useState } from "react";
import type { ChatMessage } from "../../types/message";
import { useAvatars } from "../../context/AvatarContext";
import {
	downloadTextFile,
	messageMarkdownFilename,
	singleAssistantMarkdown,
} from "../../utils/exportConversation";
import { resolveAssistantBlocks } from "../../utils/assistantBlocks";
import { DEFAULT_AGENT_AVATAR, DEFAULT_USER_AVATAR } from "../../utils/avatars";
import { imageToDataUrl } from "../../utils/images";
import { AssistantTurnBody } from "./AssistantTurnBody";
import { CompactionSummaryBlock } from "./CompactionSummaryBlock";
import { ToolCallBlock } from "./ToolCallBlock";
import { BashOutputBlock } from "./BashOutputBlock";
import styles from "./MessageList.module.css";

interface MessageBubbleProps {
	message: ChatMessage;
	agentGutter?: "avatar" | "spacer" | "none";
	toolsExpanded?: boolean;
	showThinkingAndTools?: boolean;
}

function ChatAvatar({
	src,
	fallback,
	className,
}: {
	src: string;
	fallback: string;
	className: string;
}) {
	const trimmed = src.trim();
	const [failedSrc, setFailedSrc] = useState<string | null>(null);
	const displaySrc = trimmed && failedSrc !== trimmed ? trimmed : fallback;

	return (
		<img
			className={className}
			src={displaySrc}
			width={32}
			height={32}
			alt=""
			decoding="async"
			referrerPolicy="no-referrer"
			onError={() => {
				if (trimmed) setFailedSrc(trimmed);
			}}
		/>
	);
}

function AgentSideGutter({ mode, avatarUrl }: { mode: "avatar" | "spacer"; avatarUrl: string }) {
	if (mode === "spacer") {
		return <div className={styles.assistantAvatarSpacer} aria-hidden="true" />;
	}
	return (
		<div className={styles.assistantAvatarSlot}>
			<ChatAvatar src={avatarUrl} fallback={DEFAULT_AGENT_AVATAR} className={styles.assistantAvatar} />
		</div>
	);
}

function AssistantActions({ content }: { content: string }) {
	const [copied, setCopied] = useState(false);
	const markdown = singleAssistantMarkdown(content);

	const handleCopy = async () => {
		try {
			await navigator.clipboard.writeText(markdown);
			setCopied(true);
			window.setTimeout(() => setCopied(false), 1500);
		} catch {
			/* ignore */
		}
	};

	const handleDownload = () => {
		downloadTextFile(messageMarkdownFilename(content), markdown, "text/markdown");
	};

	return (
		<div className={styles.assistantToolbar}>
			<button
				type="button"
				className={`${styles.msgActionBtn} ${copied ? styles.msgActionBtnCopied : ""}`}
				onClick={() => void handleCopy()}
				title="复制 Markdown"
			>
				{copied ? "已复制" : "复制"}
			</button>
			<button
				type="button"
				className={styles.msgActionBtn}
				onClick={handleDownload}
				title="下载 Markdown"
			>
				下载
			</button>
		</div>
	);
}

export const MessageBubble = memo(function MessageBubble({
	message,
	agentGutter = "none",
	toolsExpanded = false,
	showThinkingAndTools = true,
}: MessageBubbleProps) {
	const { userAvatarUrl, agentAvatarUrl } = useAvatars();
	const gutter =
		agentGutter === "avatar" || agentGutter === "spacer" ? (
			<AgentSideGutter mode={agentGutter} avatarUrl={agentAvatarUrl} />
		) : null;

	if (message.role === "tool_call") {
		return (
			<div className={`${styles.assistantRow} ${styles.toolCallRow}`}>
				{gutter}
				<div className={`${styles.msg} ${styles.toolCall}`}>
					<ToolCallBlock content={message.content} forceOpen={toolsExpanded} />
				</div>
			</div>
		);
	}

	if (message.role === "bash") {
		return (
			<div className={styles.assistantRow}>
				{gutter}
				<div className={`${styles.msg} ${styles.bash}`}>
					<BashOutputBlock
						command={message.bashCommand}
						content={message.content}
						exitCode={message.bashExitCode}
						streaming={message.streaming}
						excludeFromContext={message.bashExcludeFromContext}
					/>
				</div>
			</div>
		);
	}

	if (message.role === "compaction_summary") {
		return (
			<div className={`${styles.msg} ${styles.system}`}>
				<CompactionSummaryBlock content={message.content} />
			</div>
		);
	}

	if (message.role === "branch_summary") {
		return (
			<div className={`${styles.msg} ${styles.system}`}>
				<CompactionSummaryBlock content={message.content} label="分支摘要" />
			</div>
		);
	}

	if (message.role === "assistant" || message.role === "thinking") {
		const blocks = resolveAssistantBlocks(message);
		const hasVisibleText = blocks.some((b) => b.type === "text" && b.content.trim());
		const showActions = message.role === "assistant" && !message.streaming && hasVisibleText;
		const classNames = [styles.msg, styles.assistant];
		if (message.streaming) classNames.push(styles.streaming);

		return (
			<div className={styles.assistantRow}>
				{gutter}
				<div className={classNames.join(" ")}>
					{showActions ? <AssistantActions content={message.content} /> : null}
					<div className={styles.assistantBody}>
						<AssistantTurnBody
							blocks={blocks}
							streaming={message.streaming}
							showThinking={showThinkingAndTools}
						/>
					</div>
				</div>
			</div>
		);
	}

	const classNames = [styles.msg];
	if (message.role === "user") classNames.push(styles.user);
	if (message.role === "system") classNames.push(styles.system);

	if (message.role === "user") {
		return (
			<div className={styles.userRow}>
				<div className={classNames.join(" ")}>
					{message.content ? <div>{message.content}</div> : null}
					{message.images?.length ? (
						<div className={styles.userImages}>
							{message.images.map((img, index) => (
								<img
									key={`${img.mimeType}-${index}`}
									className={styles.userImage}
									src={imageToDataUrl(img)}
									alt=""
									loading="lazy"
								/>
							))}
						</div>
					) : null}
				</div>
				<ChatAvatar src={userAvatarUrl} fallback={DEFAULT_USER_AVATAR} className={styles.userAvatar} />
			</div>
		);
	}

	return <div className={classNames.join(" ")}>{message.content}</div>;
});