import { useRef, useState } from 'react';
import { useUserAvatar } from '../hooks/useUserAvatar';
import type { Message } from '../types';
import { getDisplayText, getFileAttachments, getImageUrls, getRenderableImageUrls, getThinking, shouldShowChatMessage } from '../utils/message';
import { resolveMessageRecallKey } from '../utils/memoryFeedback';
import { getMessageSaveActions } from '../utils/messageSave';
import { renderMarkdown } from '../utils/markdown';
import { filterText } from '../utils/wordFilter';
import { formatChatTime, shouldShowTimestamp } from '../utils/time';
import { TKMindAvatar } from './TKMindAvatar';
import { UserAvatar } from './UserAvatar';
import { ChatWelcomePanel } from './ChatWelcomePanel';

function CopyIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <rect x="8" y="8" width="12" height="12" rx="2" stroke="currentColor" strokeWidth="1.8" />
      <path
        d="M6 16H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinecap="round"
      />
    </svg>
  );
}

function CheckIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <path
        d="M5 12.5l4.5 4.5L19 7.5"
        stroke="currentColor"
        strokeWidth="2"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function PageIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <path
        d="M7 3.5h7.5L19 8v12.5H7z"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinejoin="round"
      />
      <path d="M14.5 3.5V8H19M10 12h6M10 15h6M10 18h4" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" />
    </svg>
  );
}

function ImageIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <rect x="4" y="5" width="16" height="14" rx="2" stroke="currentColor" strokeWidth="1.8" />
      <path d="M8 15l3-3 3 3 2-2 3 3" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" />
      <circle cx="9" cy="9" r="1.2" fill="currentColor" />
    </svg>
  );
}

function DocumentIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <path
        d="M6.5 4h8L18 7.5V20H6.5z"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinejoin="round"
      />
      <path d="M14.5 4v4H18" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" />
      <path
        d="M8.7 11l1 5 1.3-4 1.3 4 1-5"
        stroke="currentColor"
        strokeWidth="1.45"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function PlazaIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true" className="icon-plaza">
      <rect x="4" y="4" width="7" height="7" rx="1.6" stroke="currentColor" strokeWidth="1.7" />
      <rect x="13" y="4" width="7" height="7" rx="1.6" stroke="currentColor" strokeWidth="1.7" />
      <rect x="4" y="13" width="7" height="7" rx="1.6" stroke="currentColor" strokeWidth="1.7" />
      <path
        d="M13 16.5h7M16.5 13v7"
        stroke="currentColor"
        strokeWidth="1.7"
        strokeLinecap="round"
      />
    </svg>
  );
}

function LinkIcon() {
  return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <path
        d="M10.5 13.5l3-3M9.5 8.5l1.2-1.2a4 4 0 0 1 5.7 5.7l-1.2 1.2M14.5 15.5l-1.2 1.2a4 4 0 0 1-5.7-5.7l1.2-1.2"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function ToolActivityIcon({ spinning }: { spinning: boolean }) {
  return (
    <span className={`tool-badge-spinner${spinning ? ' is-spinning' : ''}`} aria-hidden="true">
      <span />
    </span>
  );
}

function ToolBadgeItem({ spinning }: { spinning: boolean }) {
  return (
    <span className="tool-badge">
      <ToolActivityIcon spinning={spinning} />
      <span>工具调用</span>
    </span>
  );
}

function hasToolActivity(message: Message) {
  return message.content.some((c) => c.type === 'toolRequest' || c.type === 'toolResponse');
}

function isToolOnlyAssistantMessage(message: Message) {
  return (
    message.role === 'assistant' &&
    hasToolActivity(message) &&
    !getDisplayText(message).trim() &&
    !getThinking(message) &&
    getImageUrls(message).length === 0
  );
}

function TypingBubble() {
  return (
    <div className="msg-bubble msg-bubble-assistant msg-typing">
      <span className="typing-dots">
        <span />
        <span />
        <span />
      </span>
    </div>
  );
}

function shouldShowEndTypingIndicator(messages: Message[], streaming: boolean) {
  if (!streaming) return false;
  const lastUserIndex = messages.findLastIndex((message) => message.role === 'user');
  if (lastUserIndex < 0) return true;
  const assistantReply = messages
    .slice(lastUserIndex + 1)
    .find((message) => message.role === 'assistant');
  if (!assistantReply) return true;
  if (getDisplayText(assistantReply).trim() || getThinking(assistantReply)) return false;
  if (hasToolActivity(assistantReply)) return false;
  return true;
}

function MemoryRecallBadgeItem({
  recall,
}: {
  recall: { count: number; previews: string[] };
}) {
  const [open, setOpen] = useState(false);
  const hasPreviews = recall.previews.length > 0;

  return (
    <div className="memory-recall-badge-wrap">
      <button
        type="button"
        className={`memory-recall-badge${open ? ' is-open' : ''}`}
        aria-expanded={hasPreviews ? open : undefined}
        onClick={() => {
          if (hasPreviews) setOpen((current) => !current);
        }}
      >
        <span>参考了你的 {recall.count} 条记忆</span>
        {hasPreviews && <ChevronIcon open={open} />}
      </button>
      {open && hasPreviews && (
        <ul className="memory-recall-previews">
          {recall.previews.map((preview, index) => (
            <li key={`${index}-${preview.slice(0, 12)}`}>{preview}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

function ToolBadge({ message, active }: { message: Message; active: boolean }) {
  if (message.role !== 'assistant') return null;
  const tools = message.content.filter((c) => c.type === 'toolRequest' || c.type === 'toolResponse');
  if (tools.length === 0) return null;

  return (
    <div className="tool-badges">
      {tools.map((tool, i) => (
        <ToolBadgeItem key={`${message.id}-${tool.id}-${i}`} spinning={active} />
      ))}
    </div>
  );
}

function CopyButton({ text }: { text: string }) {
  const [copied, setCopied] = useState(false);

  const handleCopy = async () => {
    if (!text) return;
    try {
      await navigator.clipboard.writeText(text);
      setCopied(true);
      window.setTimeout(() => setCopied(false), 1500);
    } catch {
      const textarea = document.createElement('textarea');
      textarea.value = text;
      textarea.style.position = 'fixed';
      textarea.style.opacity = '0';
      document.body.appendChild(textarea);
      textarea.select();
      document.execCommand('copy');
      document.body.removeChild(textarea);
      setCopied(true);
      window.setTimeout(() => setCopied(false), 1500);
    }
  };

  return (
    <button
      type="button"
      className={`msg-copy${copied ? ' msg-copy-done' : ''}`}
      aria-label={copied ? '已复制' : '复制'}
      title={copied ? '已复制' : '复制'}
      onClick={() => void handleCopy()}
    >
      {copied ? <CheckIcon /> : <CopyIcon />}
    </button>
  );
}

function PublicLinkCopyButton({ url }: { url: string }) {
  const [copied, setCopied] = useState(false);
  const copyTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

  const copy = async () => {
    try {
      await navigator.clipboard.writeText(url);
      setCopied(true);
      if (copyTimer.current) window.clearTimeout(copyTimer.current);
      copyTimer.current = window.setTimeout(() => setCopied(false), 1800);
    } catch {
      setCopied(false);
    }
  };

  return (
    <button
      type="button"
      className={`msg-public-share-link${copied ? ' is-copied' : ''}`}
      onClick={() => void copy()}
      aria-label={copied ? '链接已复制' : '复制链接'}
      title={copied ? '链接已复制' : '复制链接'}
    >
      {copied ? <CheckIcon /> : <LinkIcon />}
    </button>
  );
}

function TimeDivider({ timestamp }: { timestamp: number }) {
  return (
    <div className="msg-time-divider">
      <span>{formatChatTime(timestamp)}</span>
    </div>
  );
}

function ChevronIcon({ open }: { open: boolean }) {
  return (
    <svg
      width="16"
      height="16"
      viewBox="0 0 24 24"
      fill="none"
      aria-hidden="true"
      className={open ? 'msg-actions-chevron is-open' : 'msg-actions-chevron'}
    >
      <path
        d="M9 6l6 6-6 6"
        stroke="currentColor"
        strokeWidth="2"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function MessageRow({
  message,
  avatarUrl,
  onAvatarClick,
  onSaveAsPage,
  onShareToPlaza,
  onDownloadLongImage,
  onDownloadDocx,
  saveDisabled,
  publishUserId,
  publishUsername,
  sessionId,
  compact = false,
  activeToolMessage = false,
  showInlineTyping = false,
  memoryRecallByMessageId = {},
  messageIndex = 0,
}: {
  message: Message;
  avatarUrl: string | null;
  onAvatarClick?: () => void;
  onSaveAsPage?: (message: Message) => void;
  onShareToPlaza?: (message: Message) => void;
  onDownloadLongImage?: (message: Message, publicUrl: string) => void;
  onDownloadDocx?: (message: Message) => void;
  saveDisabled?: boolean;
  publishUserId?: string;
  publishUsername?: string;
  sessionId?: string;
  compact?: boolean;
  activeToolMessage?: boolean;
  showInlineTyping?: boolean;
  memoryRecallByMessageId?: Record<string, { count: number; previews: string[] }>;
  messageIndex?: number;
}) {
  const [actionsOpen, setActionsOpen] = useState(false);
  const rawText = getDisplayText(message);
  const text = filterText(rawText);
  const memoryRecall = memoryRecallByMessageId[resolveMessageRecallKey(message, messageIndex)];
  const saveActions = getMessageSaveActions(text, { userId: publishUserId, username: publishUsername });
  const thinking = getThinking(message);
  const isUser = message.role === 'user';
  const imageUrls = getImageUrls(message);
  const fileAttachments = getFileAttachments(message);
  const renderableImageUrls = getRenderableImageUrls(message);
  const copyText = [thinking ? `【思考】\n${thinking}` : '', text].filter(Boolean).join('\n\n');

  if (!text && !thinking && message.role === 'assistant') {
    return (
      <div className={`msg-row msg-row-assistant`}>
        <TKMindAvatar />
        <div className="msg-content">
          {showInlineTyping ? (
            <TypingBubble />
          ) : (
            <>
              <ToolBadge message={message} active={activeToolMessage} />
              {memoryRecall && <MemoryRecallBadgeItem recall={memoryRecall} />}
            </>
          )}
        </div>
      </div>
    );
  }

  const hasAssistantActions =
    !isUser && Boolean(message.id && onSaveAsPage && copyText);
  const hasPageDownloadActions = hasAssistantActions && saveActions.kind === 'page' && Boolean(saveActions.previewUrl);
  const hasPlazaAction = hasPageDownloadActions && Boolean(onShareToPlaza);
  const showActionsToggle = compact && Boolean(copyText);

  const renderPageActions = () => (
    <div className="msg-page-actions">
      <button
        type="button"
        className="msg-save-page"
        disabled={saveDisabled}
        onClick={() => onSaveAsPage!(message)}
        aria-label={saveActions.kind === 'page' ? '保存页面' : '保存为文章'}
        title={saveActions.kind === 'page' ? '保存页面' : '保存为文章'}
      >
        <PageIcon />
      </button>
      {saveActions.previewUrl && <PublicLinkCopyButton url={saveActions.previewUrl} />}
      {hasPageDownloadActions && (
        <>
          <button
            type="button"
            className="msg-save-page"
            onClick={() => onDownloadLongImage?.(message, saveActions.previewUrl!)}
            aria-label="下载图片"
            title="下载图片"
          >
            <ImageIcon />
          </button>
          {hasPlazaAction && (
            <button
              type="button"
              className="msg-save-page icon-plaza"
              disabled={saveDisabled}
              onClick={() => onShareToPlaza!(message)}
              aria-label="发布到 Plaza"
              title="发布到 Plaza"
            >
              <PlazaIcon />
            </button>
          )}
          <button
            type="button"
            className="msg-save-page"
            onClick={() => onDownloadDocx?.(message)}
            aria-label="保存文档"
            title="保存文档"
          >
            <DocumentIcon />
          </button>
        </>
      )}
    </div>
  );

  return (
    <div className={`msg-row ${isUser ? 'msg-row-user' : 'msg-row-assistant'}`}>
      {!isUser && <TKMindAvatar />}
      <div className="msg-content">
        <div className={`msg-bubble-wrap${compact ? ' msg-bubble-wrap-compact' : ''}`}>
          <div className={compact ? 'msg-bubble-row' : undefined}>
            <div className={`msg-bubble ${isUser ? 'msg-bubble-user' : 'msg-bubble-assistant'}`}>
              {thinking && (
                <details className="thinking">
                  <summary>思考中…</summary>
                  <pre>{thinking}</pre>
                </details>
              )}
              {imageUrls.length > 0 && (
                <div className="msg-image-gallery">
                  {renderableImageUrls.map((image, index) => (
                    <a
                      key={`${image.href}-${index}`}
                      href={image.href}
                      target="_blank"
                      rel="noreferrer"
                      className="msg-image-link"
                    >
                      <img
                        src={image.src}
                        alt={`图片 ${index + 1}`}
                        className="msg-image-thumb"
                        loading="lazy"
                      />
                    </a>
                  ))}
                </div>
              )}
              {fileAttachments.length > 0 && (
                <div className="msg-file-attachments">
                  {fileAttachments.map((file, index) => (
                    <a
                      key={`${file.downloadUrl}-${index}`}
                      href={file.downloadUrl}
                      target="_blank"
                      rel="noreferrer"
                      className="msg-file-attachment-link"
                    >
                      {file.filename}
                    </a>
                  ))}
                </div>
              )}
              {text && (
                <div
                  className="bubble-text"
                  dangerouslySetInnerHTML={{ __html: renderMarkdown(text) }}
                />
              )}
              <ToolBadge message={message} active={activeToolMessage} />
              {memoryRecall && <MemoryRecallBadgeItem recall={memoryRecall} />}
            </div>
            {showActionsToggle && (
              <button
                type="button"
                className={`msg-actions-toggle${actionsOpen ? ' is-open' : ''}`}
                aria-label={actionsOpen ? '收起操作' : '展开操作'}
                aria-expanded={actionsOpen}
                onClick={() => setActionsOpen((current) => !current)}
              >
                <ChevronIcon open={actionsOpen} />
              </button>
            )}
          </div>
          {copyText && !compact && (
            <div className="msg-actions">
              <CopyButton text={copyText} />
              {hasAssistantActions && renderPageActions()}
            </div>
          )}
          {copyText && compact && actionsOpen && (
            <div className="msg-actions msg-actions-compact">
              <CopyButton text={copyText} />
              {hasAssistantActions && renderPageActions()}
            </div>
          )}
        </div>
      </div>
      {isUser && (
        <UserAvatar
          avatarUrl={avatarUrl}
          className="msg-avatar msg-avatar-user"
          onClick={onAvatarClick}
          title={onAvatarClick ? '点击更换头像' : undefined}
        />
      )}
    </div>
  );
}

export function MessageList({
  messages,
  streaming,
  onAvatarClick,
  onSaveAsPage,
  onShareToPlaza,
  onDownloadLongImage,
  onDownloadDocx,
  publishUserId,
  publishUsername,
  compact = false,
  memoryRecallByMessageId = {},
}: {
  messages: Message[];
  streaming: boolean;
  onAvatarClick?: () => void;
  onSaveAsPage?: (message: Message) => void;
  onShareToPlaza?: (message: Message) => void;
  onDownloadLongImage?: (message: Message, publicUrl: string) => void;
  onDownloadDocx?: (message: Message) => void;
  publishUserId?: string;
  publishUsername?: string;
  compact?: boolean;
  memoryRecallByMessageId?: Record<string, { count: number; previews: string[] }>;
}) {
  const { avatarUrl } = useUserAvatar();
  const renderableMessages = messages.filter(shouldShowChatMessage);
  const lastRenderableIndex = renderableMessages.length - 1;
  const activeToolMessage =
    streaming &&
    lastRenderableIndex >= 0 &&
    hasToolActivity(renderableMessages[lastRenderableIndex])
      ? renderableMessages[lastRenderableIndex]
      : null;
  const visibleMessages = renderableMessages.filter(
    (message) => !isToolOnlyAssistantMessage(message) || message === activeToolMessage,
  );
  const showEndTyping = shouldShowEndTypingIndicator(renderableMessages, streaming);
  const lastUserIndex = renderableMessages.findLastIndex((message) => message.role === 'user');
  const inlineTypingMessage =
    lastUserIndex >= 0
      ? renderableMessages
          .slice(lastUserIndex + 1)
          .find(
            (message) =>
              message.role === 'assistant' &&
              !getDisplayText(message).trim() &&
              !getThinking(message) &&
              !hasToolActivity(message),
          ) ?? null
      : null;

  return (
    <div className="message-list">
      {messages.length === 0 && <ChatWelcomePanel compact={compact} />}
      {visibleMessages.map((message, index) => {
        const prev = index > 0 ? visibleMessages[index - 1] : undefined;
        const showTime = shouldShowTimestamp(message.created, prev?.created);
        const anchorId = message.id ?? `msg-${index}-${message.role}-${message.created}`;
        return (
          <div
            key={message.id ?? `msg-${index}`}
            className="msg-block"
            data-message-anchor={anchorId}
          >
            {showTime && <TimeDivider timestamp={message.created} />}
            <MessageRow
              message={message}
              avatarUrl={avatarUrl}
              onAvatarClick={onAvatarClick}
              onSaveAsPage={onSaveAsPage}
              onShareToPlaza={onShareToPlaza}
              onDownloadLongImage={onDownloadLongImage}
              onDownloadDocx={onDownloadDocx}
              saveDisabled={streaming}
              publishUserId={publishUserId}
              publishUsername={publishUsername}
              compact={compact}
              activeToolMessage={message === activeToolMessage}
              showInlineTyping={showEndTyping && message === inlineTypingMessage}
              memoryRecallByMessageId={memoryRecallByMessageId}
              messageIndex={index}
            />
          </div>
        );
      })}
      {showEndTyping && !inlineTypingMessage && (
        <div className="msg-row msg-row-assistant" role="status" aria-live="polite" aria-label="助手正在回复">
          <TKMindAvatar />
          <div className="msg-content">
            <TypingBubble />
          </div>
        </div>
      )}
    </div>
  );
}
