"use client";
/**
 * ChatArea 消息列表区
 *
 * 组成（自上而下）：
 *   - AI 披露横幅
 *   - 日期分隔条
 *   - 消息气泡（AI 左 / 用户右）
 *   - AI 正在回复时的加载动画气泡
 *
 * 滚动行为：
 *   - 新消息到达时自动滚到底部
 *   - 用户向上滚动时不强制拉回（保持阅读上下文）
 */
import {
  lazy,
  Suspense,
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
} from "react";

import type { UiMessage } from "@/data/dto/chat";

import {
  buildChatRenderItems,
  createChatMessageKeyResolver,
  type ChatMessageKeyResolver,
} from "../chat-render-items";
import { AiDisclosureBanner } from "./ai-disclosure-banner";
import { DateHeader } from "./date-header";
import { MessageBubble } from "./message-bubble";
import styles from "./chat-area.module.css";

const CHAT_AUTO_SCROLL_BOTTOM_THRESHOLD = 96;
const ReplyingAnimation = lazy(() =>
  import("./lottie-message-bubble").then((module) => ({
    default: module.LottieMessageBubble,
  })),
);

export interface ChatAreaProps {
  messages: readonly UiMessage[];
  isReplyingAI: boolean;
  initialScrollReady?: boolean;
  isUnlockingMessage?: boolean;
  unlockingMessageId?: string | null;
  onUnlockPrivateMessage?: (messageId: string) => void;
  onUnlockVoiceMessage?: (messageId: string) => void;
  onUnlockImageMessage?: (messageId: string) => void;
  onOpenImage?: (messageId: string) => void;
}

export function ChatArea({
  messages,
  isReplyingAI,
  initialScrollReady = true,
  isUnlockingMessage,
  unlockingMessageId,
  onUnlockPrivateMessage,
  onUnlockVoiceMessage,
  onUnlockImageMessage,
  onOpenImage,
}: ChatAreaProps) {
  const scrollRef = useRef<HTMLDivElement>(null);
  const contentRef = useRef<HTMLDivElement>(null);
  const initialScrollSettledRef = useRef(false);
  const shouldStickToBottomRef = useRef(true);
  const getMessageKey = useMemo(() => createChatMessageKeyResolver(), []);

  useLayoutEffect(() => {
    if (!initialScrollReady) {
      initialScrollSettledRef.current = false;
      return;
    }

    const scrollNode = scrollRef.current;
    if (!scrollNode) return;

    if (!initialScrollSettledRef.current) {
      initialScrollSettledRef.current = true;
      shouldStickToBottomRef.current = true;
    }

    if (!shouldStickToBottomRef.current) return;

    scrollToBottom(scrollNode);
    const frameId = requestAnimationFrame(() => {
      if (
        scrollRef.current === scrollNode &&
        shouldStickToBottomRef.current
      ) {
        scrollToBottom(scrollNode);
      }
    });

    return () => cancelAnimationFrame(frameId);
  }, [initialScrollReady, isReplyingAI, messages]);

  useEffect(() => {
    const scrollNode = scrollRef.current;
    const contentNode = contentRef.current;
    if (!scrollNode || !contentNode || typeof ResizeObserver === "undefined") {
      return;
    }

    const observer = new ResizeObserver(() => {
      if (
        initialScrollSettledRef.current &&
        shouldStickToBottomRef.current
      ) {
        scrollToBottom(scrollNode);
      }
    });
    observer.observe(scrollNode);
    observer.observe(contentNode);

    return () => observer.disconnect();
  }, []);

  return (
    <main
      ref={scrollRef}
      className={styles.area}
      aria-label="Chat messages"
      onScroll={(event) => {
        shouldStickToBottomRef.current = isNearBottom(event.currentTarget);
      }}
    >
      <div ref={contentRef} className={styles.content}>
        <AiDisclosureBanner />

        {renderMessagesWithDateHeaders(
          messages,
          getMessageKey,
          isUnlockingMessage,
          unlockingMessageId,
          onUnlockPrivateMessage,
          onUnlockVoiceMessage,
          onUnlockImageMessage,
          onOpenImage,
        )}

        {isReplyingAI ? (
          <Suspense fallback={null}>
            <ReplyingAnimation />
          </Suspense>
        ) : null}
      </div>
    </main>
  );
}

function scrollToBottom(scrollNode: HTMLElement): void {
  scrollNode.scrollTop = Math.max(
    0,
    scrollNode.scrollHeight - scrollNode.clientHeight,
  );
}

function isNearBottom(scrollNode: HTMLElement): boolean {
  const distanceFromBottom =
    scrollNode.scrollHeight - scrollNode.scrollTop - scrollNode.clientHeight;
  return distanceFromBottom <= CHAT_AUTO_SCROLL_BOTTOM_THRESHOLD;
}

/** 渲染消息列表（按日期分组插入分隔条） */
function renderMessagesWithDateHeaders(
  messages: readonly UiMessage[],
  getMessageKey: ChatMessageKeyResolver,
  isUnlockingMessage?: boolean,
  unlockingMessageId?: string | null,
  onUnlockPrivateMessage?: (messageId: string) => void,
  onUnlockVoiceMessage?: (messageId: string) => void,
  onUnlockImageMessage?: (messageId: string) => void,
  onOpenImage?: (messageId: string) => void,
) {
  return buildChatRenderItems(messages, getMessageKey).map((item) =>
    item.type === "date" ? (
      <DateHeader key={item.key} date={item.date} />
    ) : (
      <MessageBubble
        key={item.key}
        messageId={item.message.id}
        content={item.message.content}
        imageUrl={item.message.imageUrl}
        imagePaywalled={item.message.imagePaywalled}
        audioUrl={item.message.audioUrl}
        isFromAI={item.message.isFromAI}
        locked={item.message.locked}
        lockReason={item.message.lockReason}
        lockedPrivate={item.message.lockedPrivate}
        privateMessageHint={item.message.privateMessageHint}
        isUnlockingMessage={
          isUnlockingMessage === true &&
          item.message.id === unlockingMessageId
        }
        onUnlockPrivateMessage={onUnlockPrivateMessage}
        onUnlockVoiceMessage={onUnlockVoiceMessage}
        onUnlockImageMessage={onUnlockImageMessage}
        onOpenImage={onOpenImage}
      />
    ),
  );
}
