import {describe, expect, it} from 'vitest';

import {ChatTimeHistoryMapper, ChatTimeTimelineBuilder,} from '../chat-time';

describe('chat-time timeline builder', () => {
  it('builds assistant thinking and message in the same assistant item', () => {
    const items: any[] = [];

    ChatTimeTimelineBuilder.appendUserMessage(items, {
      content: '你好',
      created: 1,
      id: 'user-1',
    });
    ChatTimeTimelineBuilder.appendThinkingDelta(items, '先想一下', 2);
    ChatTimeTimelineBuilder.appendMessageDelta(items, '最终回答', 3);
    ChatTimeTimelineBuilder.finalize(items);

    expect(items).toHaveLength(2);
    expect(items[1]).toMatchObject({
      content: '最终回答',
      role: 'assistant',
    });
    expect(items[1].segments).toMatchObject([
      { content: '先想一下', status: 'end', type: 'thinking' },
      { content: '最终回答', type: 'text' },
    ]);
  });

  it('appends markdown deltas without altering repeated symbols', () => {
    const items: any[] = [];

    ChatTimeTimelineBuilder.appendThinkingDelta(items, '先想一下', 1);
    ChatTimeTimelineBuilder.appendMessageDelta(items, '## 标题\n', 2);
    ChatTimeTimelineBuilder.appendMessageDelta(items, '| 模型 | 说明 |\n', 3);
    ChatTimeTimelineBuilder.appendMessageDelta(items, '| --- | --- |\n', 4);
    ChatTimeTimelineBuilder.appendMessageDelta(items, '| ACL | 访问控制列表 |\n', 5);
    ChatTimeTimelineBuilder.appendMessageDelta(
      items,
      'Final Answer: ```echartsoption',
      6,
    );

    expect(items).toHaveLength(1);
    expect(items[0]).toMatchObject({
      content:
        '## 标题\n| 模型 | 说明 |\n| --- | --- |\n| ACL | 访问控制列表 |\nFinal Answer: ```echartsoption',
      role: 'assistant',
      typing: true,
    });
    expect(items[0].segments).toMatchObject([
      { content: '先想一下', status: 'end', type: 'thinking' },
      {
        content:
          '## 标题\n| 模型 | 说明 |\n| --- | --- |\n| ACL | 访问控制列表 |\nFinal Answer: ```echartsoption',
        type: 'text',
      },
    ]);
  });

  it('creates a new assistant item after tool result', () => {
    const items: any[] = [];

    ChatTimeTimelineBuilder.appendMessageDelta(items, '第一段回答', 1);
    ChatTimeTimelineBuilder.upsertToolCall(items, {
      name: 'search_docs',
      toolCallId: 'tool-1',
      value: '{"query":"java"}',
    });
    ChatTimeTimelineBuilder.upsertToolResult(items, {
      result: '{"hits":1}',
      toolCallId: 'tool-1',
    });
    ChatTimeTimelineBuilder.appendThinkingDelta(items, '继续思考', 2);
    ChatTimeTimelineBuilder.appendMessageDelta(items, '第二段回答', 3);
    ChatTimeTimelineBuilder.finalize(items);

    expect(items).toHaveLength(3);
    expect(items[0]).toMatchObject({ content: '第一段回答', role: 'assistant' });
    expect(items[1]).toMatchObject({
      name: 'search_docs',
      result: '{"hits":1}',
      role: 'tool',
      status: 'TOOL_RESULT',
      toolCallId: 'tool-1',
    });
    expect(items[2]).toMatchObject({ content: '第二段回答', role: 'assistant' });
    expect(items[2].segments).toMatchObject([
      { content: '继续思考', status: 'end', type: 'thinking' },
      { content: '第二段回答', type: 'text' },
    ]);
  });

  it('does not render built-in knowledge retrieval tools as normal tool cards', () => {
    const items: any[] = [];

    ChatTimeTimelineBuilder.appendMessageDelta(items, '第一段回答', 1);
    ChatTimeTimelineBuilder.upsertToolCall(items, {
      name: 'retrieve_knowledge',
      toolCallId: 'knowledge-1',
      value: '{"query":"请假安排"}',
    });
    ChatTimeTimelineBuilder.upsertToolResult(items, {
      name: 'retrieve_knowledge',
      result: '{"hits":1}',
      toolCallId: 'knowledge-1',
    });
    ChatTimeTimelineBuilder.upsertToolCall(items, {
      name: 'search_docs',
      toolCallId: 'tool-1',
      value: '{"query":"java"}',
    });

    expect(items).toHaveLength(2);
    expect(items[0]).toMatchObject({ content: '第一段回答', role: 'assistant' });
    expect(items[1]).toMatchObject({
      name: 'search_docs',
      role: 'tool',
      status: 'TOOL_CALL',
      toolCallId: 'tool-1',
    });
  });

  it('does not render built-in context reload tools as normal tool cards', () => {
    const items: any[] = [];

    ChatTimeTimelineBuilder.appendMessageDelta(items, '第一段回答', 1);
    ChatTimeTimelineBuilder.upsertToolCall(items, {
      name: 'context_reload',
      toolCallId: 'context-reload-1',
      value: '{"working_context_offload_uuid":"context-id"}',
    });
    ChatTimeTimelineBuilder.upsertToolResult(items, {
      name: 'context_reload',
      result: '{"messages":[]}',
      toolCallId: 'context-reload-1',
    });

    expect(items).toHaveLength(1);
    expect(items[0]).toMatchObject({ content: '第一段回答', role: 'assistant' });
  });

  it('does not render anonymous internal tool calls as normal tool cards', () => {
    const items: any[] = [];

    ChatTimeTimelineBuilder.appendMessageDelta(items, '第一段回答', 1);
    ChatTimeTimelineBuilder.upsertToolCall(items, {
      toolCallId: 'fragment-1',
      value: '{"arguments":"partial"}',
    });

    expect(items).toHaveLength(1);
    expect(items[0]).toMatchObject({ content: '第一段回答', role: 'assistant' });
  });

  it('does not render anonymous orphan tool results as normal tool cards', () => {
    const items: any[] = [];

    ChatTimeTimelineBuilder.appendMessageDelta(items, '第一段回答', 1);
    ChatTimeTimelineBuilder.upsertToolResult(items, {
      result: '{"ok":true}',
      toolCallId: 'fragment-1',
    });

    expect(items).toHaveLength(1);
    expect(items[0]).toMatchObject({ content: '第一段回答', role: 'assistant' });
  });
});

describe('chat-time history mapper', () => {
  it('expands structured messageChain into assistant and tool timeline items', () => {
    const items = ChatTimeHistoryMapper.fromHistoryRecords([
      {
        contentPayload: {
          messageChain: [
            {
              content: '先回答一点',
              reasoningContent: '先思考',
              role: 'assistant',
              toolCalls: [
                {
                  arguments: '{"query":"java"}',
                  id: 'tool-1',
                  name: 'search_docs',
                },
              ],
            },
            {
              content: '{"hits":1}',
              role: 'tool',
              toolCallId: 'tool-1',
            },
            {
              content: '最后总结',
              reasoningContent: '继续思考',
              role: 'assistant',
            },
          ],
        },
        created: 100,
        id: 'assistant-record',
        senderRole: 'assistant',
      },
    ]);

    expect(items).toHaveLength(3);
    expect(items[0]).toMatchObject({
      content: '先回答一点',
      role: 'assistant',
    });
    expect(items[1]).toMatchObject({
      arguments: '{"query":"java"}',
      name: 'search_docs',
      result: '{"hits":1}',
      role: 'tool',
      toolCallId: 'tool-1',
    });
    expect(items[2]).toMatchObject({
      content: '最后总结',
      role: 'assistant',
    });
    expect(items[0]?.id).not.toBe(items[2]?.id);
  });

  it('skips built-in knowledge retrieval tools when restoring structured history', () => {
    const items = ChatTimeHistoryMapper.fromHistoryRecords([
      {
        contentPayload: {
          messageChain: [
            {
              content: '先回答一点',
              role: 'assistant',
              toolCalls: [
                {
                  arguments: '{"query":"请假安排"}',
                  id: 'knowledge-1',
                  toolName: 'retrieve_knowledge',
                },
                {
                  arguments: '{"query":"java"}',
                  id: 'tool-1',
                  name: 'search_docs',
                },
              ],
            },
            {
              content: '{"hits":1}',
              role: 'tool',
              toolCallId: 'knowledge-1',
            },
            {
              content: '{"hits":2}',
              role: 'tool',
              toolCallId: 'tool-1',
            },
          ],
        },
        created: 100,
        id: 'assistant-record',
        senderRole: 'assistant',
      },
    ]);

    expect(items).toHaveLength(2);
    expect(items[0]).toMatchObject({
      content: '先回答一点',
      role: 'assistant',
    });
    expect(items[1]).toMatchObject({
      name: 'search_docs',
      result: '{"hits":2}',
      role: 'tool',
      toolCallId: 'tool-1',
    });
  });

  it('skips internal tools when restoring OpenAI-style structured history', () => {
    const items = ChatTimeHistoryMapper.fromHistoryRecords([
      {
        contentPayload: {
          messageChain: [
            {
              content: '先回答一点',
              role: 'assistant',
              tool_calls: [
                {
                  function: {
                    arguments: '{"query":"暑假安排"}',
                    name: 'retrieve_knowledge',
                  },
                  id: 'knowledge-1',
                },
                {
                  function: {
                    arguments: '{"working_context_offload_uuid":"context-id"}',
                    name: 'context_reload',
                  },
                  id: 'context-1',
                },
                {
                  function: {
                    arguments: '{"text":"partial"}',
                    name: '__fragment__',
                  },
                  id: 'fragment-1',
                },
                {
                  function: {
                    arguments: '{"query":"java"}',
                    name: 'search_docs',
                  },
                  id: 'tool-1',
                },
              ],
            },
            {
              content: '{"hits":1}',
              role: 'tool',
              tool_call_id: 'knowledge-1',
            },
            {
              content: '{"messages":[]}',
              role: 'tool',
              tool_call_id: 'context-1',
            },
            {
              content: '{"ok":true}',
              role: 'tool',
              tool_call_id: 'fragment-1',
            },
            {
              content: '{"hits":2}',
              role: 'tool',
              tool_call_id: 'tool-1',
            },
          ],
        },
        created: 100,
        id: 'assistant-record',
        senderRole: 'assistant',
      },
    ]);

    expect(items).toHaveLength(2);
    expect(items[0]).toMatchObject({
      content: '先回答一点',
      role: 'assistant',
    });
    expect(items[1]).toMatchObject({
      arguments: '{"query":"java"}',
      name: 'search_docs',
      result: '{"hits":2}',
      role: 'tool',
      toolCallId: 'tool-1',
    });
  });

  it('skips nameless tool result records when restoring history', () => {
    const items = ChatTimeHistoryMapper.fromHistoryRecords([
      {
        content: '{"hits":1}',
        contentPayload: {
          result: '{"hits":1}',
          toolCallId: 'knowledge-1',
        },
        created: 100,
        id: 'tool-record',
        senderRole: 'tool',
      },
      {
        content: '最终回答',
        created: 101,
        id: 'assistant-record',
        senderRole: 'assistant',
      },
    ]);

    expect(items).toHaveLength(1);
    expect(items[0]).toMatchObject({
      content: '最终回答',
      role: 'assistant',
    });
  });

  it('falls back to legacy chains when messageChain is unavailable', () => {
    const items = ChatTimeHistoryMapper.fromLegacyMessages([
      {
        chains: [
          {
            reasoning_content: '旧思考',
            thinkingStatus: 'end',
          },
          {
            id: 'tool-2',
            name: 'legacy_tool',
            result: '{"ok":true}',
            status: 'TOOL_RESULT',
          },
        ],
        content: '旧回答',
        created: '2026-05-11 10:00:00',
        id: 'legacy-1',
        role: 'assistant',
      },
    ]);

    expect(items).toHaveLength(2);
    expect(items[0]).toMatchObject({
      content: '旧回答',
      role: 'assistant',
    });
    expect(items[0].segments).toMatchObject([
      { content: '旧思考', status: 'end', type: 'thinking' },
      { content: '旧回答', type: 'text' },
    ]);
    expect(items[1]).toMatchObject({
      name: 'legacy_tool',
      result: '{"ok":true}',
      role: 'tool',
      toolCallId: 'tool-2',
    });
  });
});
