<template>
  <div class="messages-area" ref="rootEl">
    <div class="messages-flow">
      <div v-for="(msg, index) in filteredMessages" :key="index" class="message-block">
        <div v-if="msg.role === 'user'" class="user-message">
          <div class="message-header icon-label">
            <span class="icon icon-sm" :style="iconStyleSafe('user')" aria-hidden="true"></span>
            <span>用户</span>
          </div>
          <div class="message-text user-bubble-text">
            <div v-if="msg.content" class="bubble-text">{{ msg.content }}</div>
            <div v-if="msg.images && msg.images.length" class="image-inline-row">
              <span class="image-name" v-for="img in msg.images" :key="img">{{ formatImageName(img) }}</span>
            </div>
          </div>
        </div>
        <div v-else-if="msg.role === 'assistant'" class="assistant-message">
          <div class="message-header icon-label">
            <span class="icon icon-sm" :style="iconStyleSafe('bot')" aria-hidden="true"></span>
            <span>AI Assistant</span>
          </div>
          <div
            v-if="msg.awaitingFirstContent"
            class="action-item streaming-content immediate-show assistant-generating-block"
          >
            <div class="text-output">
              <div
                class="text-content assistant-generating-placeholder"
                role="status"
                aria-live="polite"
              >
                <span
                  v-for="(letter, letterIndex) in getGeneratingLetters(msg)"
                  :key="letterIndex"
                  class="assistant-generating-letter"
                  :style="{ animationDelay: `${letterIndex * 0.08}s` }"
                >
                  {{ letter }}
                </span>
              </div>
            </div>
          </div>
          <template v-if="stackedBlocksEnabled">
            <template v-for="(group, groupIndex) in splitActionGroups(msg.actions || [], index)" :key="group.key">
              <StackedBlocks
                v-if="group.kind === 'stack'"
                class="stacked-blocks-wrapper"
                :actions="group.actions"
                :expanded-blocks="expandedBlocks"
                :icon-style="iconStyleSafe"
                :toggle-block="toggleBlock"
                :register-thinking-ref="registerThinkingRef"
                :handle-thinking-scroll="handleThinkingScroll"
                :get-tool-animation-class="getToolAnimationClass"
                :get-tool-icon="getToolIcon"
                :get-tool-status-text="getToolStatusText"
                :get-tool-description="getToolDescription"
                :format-search-topic="formatSearchTopic"
                :format-search-time="formatSearchTime"
              />
              <div
                v-else
                class="action-item"
                :key="group.action?.id || `${index}-${group.actionIndex}`"
                :class="{
                  'streaming-content': group.action?.streaming,
                  'completed-tool': group.action?.type === 'tool' && !group.action?.streaming,
                  'immediate-show':
                    group.action?.streaming || group.action?.type === 'text' || group.action?.type === 'thinking',
                  'thinking-finished': group.action?.type === 'thinking' && !group.action?.streaming
                }"
              >
                <div
                  v-if="group.action?.type === 'thinking'"
                  class="collapsible-block thinking-block"
                  :class="{ expanded: expandedBlocks?.has(group.action.blockId || `${index}-thinking-${group.actionIndex}`) }"
                >
                  <div class="collapsible-header" @click="toggleBlock(group.action.blockId || `${index}-thinking-${group.actionIndex}`)">
                    <div class="arrow"></div>
                    <div class="status-icon">
                      <span class="thinking-icon" :class="{ 'thinking-animation': group.action.streaming }">
                        <span class="icon icon-sm" :style="iconStyleSafe('brain')" aria-hidden="true"></span>
                      </span>
                    </div>
                    <span class="status-text">{{ group.action.streaming ? '正在思考...' : '思考过程' }}</span>
                  </div>
                  <div
                    class="collapsible-content"
                    :ref="el => registerCollapseContent(group.action.blockId || `${index}-thinking-${group.actionIndex}`, el)"
                  >
                    <div
                      class="content-inner thinking-content"
                      :ref="el => registerThinkingRef(group.action.blockId || `${index}-thinking-${group.actionIndex}`, el)"
                      @scroll="
                        handleThinkingScroll(
                          group.action.blockId || `${index}-thinking-${group.actionIndex}`,
                          $event
                        )
                      "
                      style="max-height: 240px; overflow-y: auto;"
                >
                  {{ group.action.content }}
                </div>
              </div>
            </div>

                <div v-else-if="group.action?.type === 'text'" class="text-output">
                  <div class="text-content" :class="{ 'streaming-text': group.action.streaming }">
                    <div v-if="group.action.streaming" v-html="renderMarkdown(group.action.content, true)"></div>
                    <div v-else v-html="renderMarkdown(group.action.content, false)"></div>
                  </div>
                </div>

                <div v-else-if="group.action?.type === 'system'" class="system-action">
                  <div class="system-action-content">
                    {{ group.action.content }}
                  </div>
                </div>

                <div
                  v-else-if="group.action?.type === 'append_payload'"
                  class="append-placeholder"
                  :class="{ 'append-error': group.action.append?.success === false }"
                >
                  <div class="append-placeholder-content">
                    <template v-if="group.action.append?.success !== false">
                      <div class="icon-label append-status">
                        <span class="icon icon-sm" :style="iconStyleSafe('pencil')" aria-hidden="true"></span>
                        <span>已写入 {{ group.action.append?.path || '目标文件' }} 的追加内容（内容已保存至文件）</span>
                      </div>
                    </template>
                    <template v-else>
                      <div class="icon-label append-status append-error-text">
                        <span class="icon icon-sm" :style="iconStyleSafe('x')" aria-hidden="true"></span>
                        <span>向 {{ group.action.append?.path || '目标文件' }} 写入失败，内容已截获供后续修复。</span>
                      </div>
                    </template>
                    <div class="append-meta" v-if="group.action.append">
                      <span v-if="group.action.append.lines !== null && group.action.append.lines !== undefined">
                        · 行数 {{ group.action.append.lines }}
                      </span>
                      <span v-if="group.action.append.bytes !== null && group.action.append.bytes !== undefined">
                        · 字节 {{ group.action.append.bytes }}
                      </span>
                    </div>
                    <div class="append-warning icon-label" v-if="group.action.append?.forced">
                      <span class="icon icon-sm" :style="iconStyleSafe('triangleAlert')" aria-hidden="true"></span>
                      <span>未检测到结束标记，请根据提示继续补充。</span>
                    </div>
                  </div>
                </div>

                <div
                  v-else-if="group.action?.type === 'append'"
                  class="append-placeholder"
                  :class="{ 'append-error': group.action.append?.success === false }"
                >
                  <div class="append-placeholder-content">
                    <div class="icon-label append-status">
                      <span class="icon icon-sm" :style="iconStyleSafe('pencil')" aria-hidden="true"></span>
                      <span>{{ group.action.append?.summary || '文件追加完成' }}</span>
                    </div>
                    <div class="append-meta" v-if="group.action.append">
                      <span>{{ group.action.append.path || '目标文件' }}</span>
                      <span v-if="group.action.append.lines">· 行数 {{ group.action.append.lines }}</span>
                      <span v-if="group.action.append.bytes">· 字节 {{ group.action.append.bytes }}</span>
                    </div>
                    <div class="append-warning icon-label" v-if="group.action.append?.forced">
                      <span class="icon icon-sm" :style="iconStyleSafe('triangleAlert')" aria-hidden="true"></span>
                      <span>未检测到结束标记，请按提示继续补充。</span>
                    </div>
                  </div>
                </div>

                <ToolAction
                  v-else-if="group.action?.type === 'tool'"
                  :action="group.action"
                  :expanded="expandedBlocks?.has(group.action.blockId || `${index}-tool-${group.actionIndex}`)"
                  :icon-style="iconStyleSafe"
                  :get-tool-animation-class="getToolAnimationClass"
                  :get-tool-icon="getToolIcon"
                  :get-tool-status-text="getToolStatusText"
                  :get-tool-description="getToolDescription"
                :format-search-topic="formatSearchTopic"
                :format-search-time="formatSearchTime"
                :streaming-message="streamingMessage"
                :register-collapse-content="registerCollapseContent"
                :collapse-key="group.action.blockId || `${index}-tool-${group.actionIndex}`"
                @toggle="toggleBlock(group.action.blockId || `${index}-tool-${group.actionIndex}`)"
              />
              </div>
            </template>
          </template>

          <template v-else>
            <div
              v-for="(action, actionIndex) in msg.actions || []"
              :key="action.id || `${index}-${actionIndex}`"
              class="action-item"
              :class="{
                'streaming-content': action.streaming,
                'completed-tool': action.type === 'tool' && !action.streaming,
                'immediate-show': action.streaming || action.type === 'text' || action.type === 'thinking',
                'thinking-finished': action.type === 'thinking' && !action.streaming
              }"
            >
              <div
                v-if="action.type === 'thinking'"
                class="collapsible-block thinking-block"
                :class="{ expanded: expandedBlocks?.has(action.blockId || `${index}-thinking-${actionIndex}`) }"
              >
                <div class="collapsible-header" @click="toggleBlock(action.blockId || `${index}-thinking-${actionIndex}`)">
                  <div class="arrow"></div>
                  <div class="status-icon">
                    <span class="thinking-icon" :class="{ 'thinking-animation': action.streaming }">
                      <span class="icon icon-sm" :style="iconStyleSafe('brain')" aria-hidden="true"></span>
                    </span>
                  </div>
                  <span class="status-text">{{ action.streaming ? '正在思考...' : '思考过程' }}</span>
                </div>
                <div
                  class="collapsible-content"
                  :ref="el => registerCollapseContent(action.blockId || `${index}-thinking-${actionIndex}`, el)"
                >
                  <div
                    class="content-inner thinking-content"
                    :ref="el => registerThinkingRef(action.blockId || `${index}-thinking-${actionIndex}`, el)"
                    @scroll="
                      handleThinkingScroll(
                        action.blockId || `${index}-thinking-${actionIndex}`,
                        $event
                      )
                    "
                    style="max-height: 240px; overflow-y: auto;"
                  >
                    {{ action.content }}
                  </div>
                </div>
              </div>

              <div v-else-if="action.type === 'text'" class="text-output">
                <div class="text-content" :class="{ 'streaming-text': action.streaming }">
                  <div v-if="action.streaming" v-html="renderMarkdown(action.content, true)"></div>
                  <div v-else v-html="renderMarkdown(action.content, false)"></div>
                </div>
              </div>

              <div v-else-if="action.type === 'system'" class="system-action">
                <div class="system-action-content">
                  {{ action.content }}
                </div>
              </div>

              <div
                v-else-if="action.type === 'append_payload'"
                class="append-placeholder"
                :class="{ 'append-error': action.append?.success === false }"
              >
                <div class="append-placeholder-content">
                  <template v-if="action.append?.success !== false">
                    <div class="icon-label append-status">
                      <span class="icon icon-sm" :style="iconStyleSafe('pencil')" aria-hidden="true"></span>
                      <span>已写入 {{ action.append?.path || '目标文件' }} 的追加内容（内容已保存至文件）</span>
                    </div>
                  </template>
                  <template v-else>
                    <div class="icon-label append-status append-error-text">
                      <span class="icon icon-sm" :style="iconStyleSafe('x')" aria-hidden="true"></span>
                      <span>向 {{ action.append?.path || '目标文件' }} 写入失败，内容已截获供后续修复。</span>
                    </div>
                  </template>
                  <div class="append-meta" v-if="action.append">
                    <span v-if="action.append.lines !== null && action.append.lines !== undefined">
                      · 行数 {{ action.append.lines }}
                    </span>
                    <span v-if="action.append.bytes !== null && action.append.bytes !== undefined">
                      · 字节 {{ action.append.bytes }}
                    </span>
                  </div>
                  <div class="append-warning icon-label" v-if="action.append?.forced">
                    <span class="icon icon-sm" :style="iconStyleSafe('triangleAlert')" aria-hidden="true"></span>
                    <span>未检测到结束标记，请根据提示继续补充。</span>
                  </div>
                </div>
              </div>

              <div
                v-else-if="action.type === 'append'"
                class="append-placeholder"
                :class="{ 'append-error': action.append?.success === false }"
              >
                <div class="append-placeholder-content">
                  <div class="icon-label append-status">
                    <span class="icon icon-sm" :style="iconStyleSafe('pencil')" aria-hidden="true"></span>
                    <span>{{ action.append?.summary || '文件追加完成' }}</span>
                  </div>
                  <div class="append-meta" v-if="action.append">
                    <span>{{ action.append.path || '目标文件' }}</span>
                    <span v-if="action.append.lines">· 行数 {{ action.append.lines }}</span>
                    <span v-if="action.append.bytes">· 字节 {{ action.append.bytes }}</span>
                  </div>
                  <div class="append-warning icon-label" v-if="action.append?.forced">
                    <span class="icon icon-sm" :style="iconStyleSafe('triangleAlert')" aria-hidden="true"></span>
                    <span>未检测到结束标记，请按提示继续补充。</span>
                  </div>
                </div>
              </div>

              <ToolAction
                v-else-if="action.type === 'tool'"
                :action="action"
                :expanded="expandedBlocks?.has(action.blockId || `${index}-tool-${actionIndex}`)"
                :icon-style="iconStyleSafe"
                :get-tool-animation-class="getToolAnimationClass"
                :get-tool-icon="getToolIcon"
                :get-tool-status-text="getToolStatusText"
                :get-tool-description="getToolDescription"
                :format-search-topic="formatSearchTopic"
                :format-search-time="formatSearchTime"
                :streaming-message="streamingMessage"
                :register-collapse-content="registerCollapseContent"
                :collapse-key="action.blockId || `${index}-tool-${actionIndex}`"
                @toggle="toggleBlock(action.blockId || `${index}-tool-${actionIndex}`)"
              />
            </div>
          </template>
        </div>
        <div v-else class="system-message">
          <div class="collapsible-block system-block" :class="{ expanded: expandedBlocks?.has(`system-${index}`) }">
            <div class="collapsible-header" @click="toggleBlock(`system-${index}`)">
              <div class="arrow"></div>
              <div class="status-icon">
                <span class="tool-icon icon icon-md" :style="iconStyleSafe('info')" aria-hidden="true"></span>
              </div>
              <span class="status-text">系统消息</span>
            </div>
            <div
              class="collapsible-content"
              :ref="el => registerCollapseContent(`system-${index}`, el)"
            >
              <div class="content-inner">
                {{ msg.content }}
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';
import ToolAction from '@/components/chat/actions/ToolAction.vue';
import StackedBlocks from './StackedBlocks.vue';
import { usePersonalizationStore } from '@/stores/personalization';

const props = defineProps<{
  messages: Array<any>;
  iconStyle: (key: string, size?: string) => Record<string, string>;
  expandedBlocks: Set<string>;
  streamingMessage: boolean;
  toggleBlock: (blockId: string) => void;
  handleThinkingScroll: (blockId: string, event: Event) => void;
  renderMarkdown: (content: string, isStreaming: boolean) => string;
  getToolIcon: (tool: any) => string;
  getToolStatusText: (tool: any) => string;
  getToolAnimationClass: (tool: any) => string | Record<string, unknown>;
  getToolDescription: (tool: any) => string;
  formatSearchTopic: (filters: Record<string, any>) => string;
  formatSearchTime: (filters: Record<string, any>) => string;
}>();

const personalization = usePersonalizationStore();
const stackedBlocksEnabled = computed(() => personalization.experiments.stackedBlocksEnabled);
const filteredMessages = computed(() =>
  (props.messages || []).filter(m => !(m && m.metadata && m.metadata.system_injected_image))
);

const DEFAULT_GENERATING_TEXT = '生成中…';
const rootEl = ref<HTMLElement | null>(null);
const thinkingRefs = new Map<string, HTMLElement | null>();
const registerCollapseContent = (key: string, el: Element | null) => {
  if (!(el instanceof HTMLElement)) {
    return;
  }
  requestAnimationFrame(() => {
    const h = el.scrollHeight || el.offsetHeight || 0;
    if (h > 0) {
      el.style.setProperty('--collapse-max', `${h}px`);
    }
  });
};

function registerThinkingRef(key: string, el: Element | null) {
  if (el instanceof HTMLElement) {
    thinkingRefs.set(key, el);
  } else {
    thinkingRefs.delete(key);
  }
}

function getThinkingRef(key: string) {
  return thinkingRefs.get(key) || null;
}

function iconStyleSafe(key: string, size?: string) {
  if (typeof props.iconStyle === 'function') {
    return props.iconStyle(key, size);
  }
  return {};
}

function formatImageName(path: string): string {
  if (!path) return '';
  const parts = path.split(/[/\\]/);
  return parts[parts.length - 1] || path;
}

const isStackable = (action: any) => action && (action.type === 'thinking' || action.type === 'tool');
const splitActionGroups = (actions: any[] = [], messageIndex = 0) => {
  const result: Array<
    | { kind: 'stack'; actions: any[]; key: string }
    | { kind: 'single'; action: any; actionIndex: number; key: string }
  > = [];
  let buffer: any[] = [];

  const flushBuffer = () => {
    if (buffer.length >= 2) {
      result.push({
        kind: 'stack',
        actions: buffer.slice(),
        key: `stack-${messageIndex}-${result.length}`
      });
    } else if (buffer.length === 1) {
      const single = buffer[0];
      result.push({
        kind: 'single',
        action: single,
        actionIndex: actions.indexOf(single),
        key: single.id || `single-${messageIndex}-${result.length}`
      });
    }
    buffer = [];
  };

  actions.forEach((action, idx) => {
    if (isStackable(action)) {
      buffer.push(action);
    } else {
      flushBuffer();
      result.push({
        kind: 'single',
        action,
        actionIndex: idx,
        key: action.id || `single-${messageIndex}-${idx}`
      });
    }
  });
  flushBuffer();
  return result;
};

function getGeneratingLetters(message: any) {
  const label =
    typeof message?.generatingLabel === 'string' && message.generatingLabel.trim()
      ? message.generatingLabel.trim()
      : DEFAULT_GENERATING_TEXT;
  return Array.from(label);
}

defineExpose({
  rootEl,
  getThinkingRef
});
</script>
