import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import type { ChatMessage, MessageActionCallbacks, ParaSource } from '../../types';
import styles from './index.module.less';

marked.setOptions({ breaks: true, gfm: true });

interface MessageBubbleProps {
  message: ChatMessage;
  actions?: MessageActionCallbacks;
  onSuggestionClick?: (text: string) => void;
  onParaSourceClick?: (source: ParaSource) => void;
  eventContent?: Record<string, React.ReactNode>;
}

const TypewriterText: React.FC<{ text: string; speed?: number }> = ({
  text,
  speed = 150,
}) => {
  const [display, setDisplay] = useState('');
  const [idx, setIdx] = useState(0);

  useEffect(() => {
    if (idx < text.length) {
      const t = setTimeout(() => {
        setDisplay(prev => prev + text[idx]);
        setIdx(prev => prev + 1);
      }, speed);
      return () => clearTimeout(t);
    }
    const reset = setTimeout(() => {
      setDisplay('');
      setIdx(0);
    }, 1000);
    return () => clearTimeout(reset);
  }, [idx, text, speed]);

  return <span>{display}</span>;
};

/**
 * 将消息文本按 [ANCHOR:xxx] 拆分为纯文本段 + eventContent 注入的 ReactNode。
 * 与 largemodel 的 parseContentWithAnchors 逻辑对齐。
 */
function parseContentWithAnchors(
  content: string,
  messageId: string,
  eventContent: Record<string, React.ReactNode>,
): Array<string | React.ReactNode> {
  if (!content || !content.includes('[ANCHOR:')) return [content];

  const anchorRegex = /\[ANCHOR:([^\]]+)\]/g;
  const parts: Array<string | React.ReactNode> = [];
  let lastIndex = 0;
  let match: RegExpExecArray | null;

  while ((match = anchorRegex.exec(content)) !== null) {
    if (match.index > lastIndex) {
      const textPart = content.substring(lastIndex, match.index);
      if (textPart.trim()) parts.push(textPart);
    }

    const anchorId = match[1];
    const key = `${messageId}#${anchorId}`;
    const node = eventContent[key] ?? eventContent[anchorId];
    if (node) {
      parts.push(node);
    }

    lastIndex = match.index + match[0].length;
  }

  if (lastIndex < content.length) {
    const tail = content.substring(lastIndex);
    if (tail.trim()) parts.push(tail);
  }

  return parts;
}

export const MessageBubble: React.FC<MessageBubbleProps> = ({
  message,
  actions,
  onSuggestionClick,
  onParaSourceClick,
  eventContent,
}) => {
  const isUser = message.role === 'user';
  const isThinking = !isUser && message.status === 'loading' && !message.content;
  const isStreaming = !isUser && message.status === 'loading' && !!message.content;
  const isStopped = !isUser && message.status === 'stopped';
  const isCompleted = !isUser && (message.status === 'success' || isStopped);
  const [copied, setCopied] = useState(false);
  const [feedback, setFeedback] = useState<'like' | 'dislike' | null>(null);
  const copiedResetTimerRef = useRef<number | null>(null);

  const hasAnchors = !isUser && !!eventContent && !!message.content && message.content.includes('[ANCHOR:');

  const anchorParts = useMemo(() => {
    if (!hasAnchors || !eventContent) return null;
    return parseContentWithAnchors(message.content, message.id, eventContent);
  }, [hasAnchors, message.content, message.id, eventContent]);

  const markdownHtml = useMemo(() => {
    if (!message.content || isUser || isStreaming) return '';
    if (hasAnchors) return '';
    const raw = marked.parse(message.content) as string;
    return DOMPurify.sanitize(raw);
  }, [message.content, isUser, isStreaming, hasAnchors]);

  useEffect(() => {
    return () => {
      if (copiedResetTimerRef.current !== null) {
        window.clearTimeout(copiedResetTimerRef.current);
      }
    };
  }, []);

  const handleCopy = useCallback(async () => {
    if (actions?.onCopy) {
      actions.onCopy(message);
      return;
    }

    if (!message.content || !navigator?.clipboard) return;

    const cleanContent = message.content.replace(/\[ANCHOR:[^\]]+\]/g, '').trim();
    const htmlForCopy =
      markdownHtml || DOMPurify.sanitize(marked.parse(cleanContent) as string);

    try {
      if (typeof ClipboardItem !== 'undefined' && htmlForCopy) {
        await navigator.clipboard.write([
          new ClipboardItem({
            'text/html': new Blob([htmlForCopy], { type: 'text/html' }),
            'text/plain': new Blob([cleanContent], { type: 'text/plain' }),
          }),
        ]);
      } else {
        await navigator.clipboard.writeText(cleanContent);
      }

      setCopied(true);
      if (copiedResetTimerRef.current !== null) {
        window.clearTimeout(copiedResetTimerRef.current);
      }
      copiedResetTimerRef.current = window.setTimeout(() => {
        setCopied(false);
      }, 2000);
    } catch {
      // Swallow clipboard permission/runtime failures to avoid blocking UI interaction.
    }
  }, [actions, markdownHtml, message]);

  const handleLike = useCallback(() => {
    setFeedback(prev => (prev === 'like' ? null : 'like'));
  }, []);

  const handleDislike = useCallback(() => {
    setFeedback(prev => (prev === 'dislike' ? null : 'dislike'));
  }, []);

  return (
    <div className={`${styles.row} ${isUser ? styles.rowUser : styles.rowAi}`}>
      {!isUser && (
        <div className={styles.aiContent}>
          {isThinking ? (
            <div className={styles.thinking}>
              <span className={styles.spinner} />
              <TypewriterText text="正在思考中" speed={150} />
            </div>
          ) : (
            <>
              {message.content ? (
                isCompleted ? (
                  hasAnchors && anchorParts ? (
                    <div className={`${styles.text} ${styles.markdown}`}>
                      {anchorParts.map((part, i) =>
                        typeof part === 'string' ? (
                          <div
                            key={i}
                            dangerouslySetInnerHTML={{
                              __html: DOMPurify.sanitize(marked.parse(part) as string),
                            }}
                          />
                        ) : (
                          <div key={i}>{part}</div>
                        ),
                      )}
                    </div>
                  ) : (
                    <div
                      className={`${styles.text} ${styles.markdown}`}
                      dangerouslySetInnerHTML={{ __html: markdownHtml }}
                    />
                  )
                ) : (
                  <p className={styles.text}>
                    {message.content}
                    {isStreaming && <span className={styles.cursor} />}
                  </p>
                )
              ) : isStopped ? null : (
                <p className={styles.text}>{message.content}</p>
              )}
              {isStopped && (
                <span className={styles.stoppedHint}>已停止生成</span>
              )}

              {message.suggestedQuestions && message.suggestedQuestions.length > 0 && (
                <div className={styles.suggestions}>
                  {message.suggestedQuestions.map(q => (
                    <div
                      key={q.id}
                      className={styles.suggestionItem}
                      onClick={() => onSuggestionClick?.(q.text)}
                      role={onSuggestionClick ? 'button' : undefined}
                      tabIndex={onSuggestionClick ? 0 : undefined}
                    >
                      <span className={styles.suggestionText}>{q.text}</span>
                      <span className={styles.suggestionArrow}>
                        <ChevronIcon />
                      </span>
                    </div>
                  ))}
                </div>
              )}

              {message.status === 'success' && !message.hideActions && (
                <>
                  <p className={styles.disclaimer}>由 AI 模型生成，仅供参考。</p>
                  <div className={styles.actions}>
                    <button
                      type="button"
                      className={styles.actionBtn}
                      onClick={handleCopy}
                      title={copied ? '已复制' : '复制'}
                    >
                      {copied ? <CheckIcon /> : <CopyIcon />}
                    </button>
                    <button
                      type="button"
                      className={`${styles.actionBtn} ${feedback === 'like' ? styles.actionBtnActive : ''}`}
                      onClick={handleLike}
                      title="点赞"
                    >
                      <ThumbsUpIcon filled={feedback === 'like'} />
                    </button>
                    <button
                      type="button"
                      className={`${styles.actionBtn} ${feedback === 'dislike' ? styles.actionBtnActive : ''}`}
                      onClick={handleDislike}
                      title="点踩"
                    >
                      <ThumbsDownIcon filled={feedback === 'dislike'} />
                    </button>
                  </div>
                </>
              )}

              {message.status === 'success' && message.para && message.para.length > 0 && (
                <div className={styles.paraSources}>
                  <span className={styles.paraLabel}>数据来源：</span>
                  <div className={styles.paraList}>
                    {message.para.map((p, i) => {
                      const link = p.link?.trim();
                      const clickable = !!link && !!onParaSourceClick;
                      return (
                        <span
                          key={i}
                          className={`${styles.paraItem}${clickable ? ` ${styles.paraItemLink}` : ''}`}
                          onClick={
                            clickable
                              ? () => onParaSourceClick(p)
                              : undefined
                          }
                          role={clickable ? 'button' : undefined}
                          tabIndex={clickable ? 0 : undefined}
                        >
                          {p.title}
                        </span>
                      );
                    })}
                  </div>
                </div>
              )}
            </>
          )}
        </div>
      )}

      {isUser && (
        <div className={styles.userBubble}>
          <p className={styles.text}>{message.content}</p>
        </div>
      )}
    </div>
  );
};

const CopyIcon = () => (
  <svg width="32" height="32" viewBox="0 0 32 32" fill="none">
    <rect x="11" y="4" width="17" height="17" rx="2.5" stroke="#303540" strokeWidth="1.8" />
    <rect x="4" y="11" width="17" height="17" rx="2.5" stroke="#303540" strokeWidth="1.8" />
  </svg>
);

const CheckIcon = () => (
  <svg width="32" height="32" viewBox="0 0 32 32" fill="none">
    <path
      d="M9 16l5 5 9-10"
      stroke="#303540"
      strokeWidth="1.8"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
  </svg>
);

const ThumbsUpIcon: React.FC<{ filled?: boolean }> = ({ filled }) => (
  <svg width="32" height="32" viewBox="0 0 32 32" fill="none">
    <path
      d="M10 14h3V26h-3a1 1 0 01-1-1V15a1 1 0 011-1zm5.5 12h8.1a2 2 0 001.96-1.6l1.4-7A2 2 0 0025 15h-6.5V8.5a2.5 2.5 0 00-5 0v1L11 14"
      stroke="#303540"
      strokeWidth="1.8"
      strokeLinecap="round"
      strokeLinejoin="round"
      fill={filled ? '#303540' : 'none'}
    />
  </svg>
);

const ThumbsDownIcon: React.FC<{ filled?: boolean }> = ({ filled }) => (
  <svg width="32" height="32" viewBox="0 0 32 32" fill="none" style={{ transform: 'rotate(180deg)' }}>
    <path
      d="M10 14h3V26h-3a1 1 0 01-1-1V15a1 1 0 011-1zm5.5 12h8.1a2 2 0 001.96-1.6l1.4-7A2 2 0 0025 15h-6.5V8.5a2.5 2.5 0 00-5 0v1L11 14"
      stroke="#303540"
      strokeWidth="1.8"
      strokeLinecap="round"
      strokeLinejoin="round"
      fill={filled ? '#303540' : 'none'}
    />
  </svg>
);

const ChevronIcon = () => (
  <svg width="28" height="28" viewBox="0 0 28 28" fill="none">
    <path
      d="M10 11l4 4 4-4"
      stroke="#999"
      strokeWidth="1.5"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
  </svg>
);

