import type {
  ChatTimelineMessageItem,
  ChatTimelineStatusItem,
} from '@easyflow/common-ui';

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

import { recordsToTimelineItems } from './agentTimelineAdapter';

describe('agentTimelineAdapter', () => {
  it('restores successful turn duration from persisted message timestamps', () => {
    const items = recordsToTimelineItems([
      {
        created: '2026-08-15T10:00:00Z',
        id: 'timer-user',
        roundId: 'round-timer',
        senderRole: 'user',
        contentText: '开始计时',
      },
      {
        created: '2026-08-15T10:00:18Z',
        id: 'timer-assistant',
        roundId: 'round-timer',
        senderRole: 'assistant',
        contentText: '计时完成',
        contentPayload: {
          agentResult: { text: '计时完成' },
          terminalStatus: 'COMPLETED',
        },
      },
    ]);

    const turnItems = items.filter((item) => item.roundId === 'round-timer');
    expect(turnItems).not.toHaveLength(0);
    expect(
      turnItems.every(
        (item) =>
          item.turnStartedAt === Date.parse('2026-08-15T10:00:00Z') &&
          item.turnFinishedAt === Date.parse('2026-08-15T10:00:18Z'),
      ),
    ).toBe(true);
  });

  it('restores available and expired Artifacts with the shared safe projection', () => {
    const items = recordsToTimelineItems([
      {
        id: 'artifact-history',
        senderRole: 'assistant',
        contentText: '报告已生成',
        roundId: 'round-artifact-history',
        contentPayload: {
          agentResult: { text: '报告已生成' },
          artifacts: [
            {
              artifactId: '01JAVAILABLE',
              bucket: 'easyflow-agent-artifacts',
              fileName: '报告.pdf',
              objectKey: 'formal/private',
              sha256: 'b'.repeat(64),
              size: 4096,
              status: 'AVAILABLE',
            },
            {
              artifactId: '01JEXPIRED',
              fileName: '旧报告.xlsx',
              status: 'EXPIRED',
            },
          ],
        },
      },
    ]);

    const artifacts = items.filter((item) => item.type === 'artifact');
    expect(artifacts).toHaveLength(2);
    expect(artifacts).toEqual([
      expect.objectContaining({
        downloadUrl: '/api/v1/agent/artifacts/01JAVAILABLE/content',
        status: 'available',
      }),
      expect.objectContaining({
        downloadUrl: undefined,
        status: 'expired',
      }),
    ]);
    expect(JSON.stringify(artifacts)).not.toMatch(
      /easyflow-agent-artifacts|objectKey|formal\/private/,
    );
  });

  it('uses refreshed direct Artifact status instead of stale runtime events', () => {
    const items = recordsToTimelineItems([
      {
        id: 'artifact-status-conflict',
        senderRole: 'assistant',
        contentText: '产物已过期',
        roundId: 'round-artifact-status-conflict',
        contentPayload: {
          artifacts: [
            {
              artifactId: '01JSTATUSCONFLICT',
              fileName: '账本报告.pdf',
              status: 'EXPIRED',
            },
          ],
          runtimeEvents: [
            {
              name: 'easyflow.artifact.published',
              value: {
                artifactId: '01JSTATUSCONFLICT',
                fileName: '旧事件报告.pdf',
                status: 'AVAILABLE',
              },
            },
          ],
        },
      },
    ]);

    const artifacts = items.filter((item) => item.type === 'artifact');
    expect(artifacts).toEqual([
      expect.objectContaining({
        artifactId: '01JSTATUSCONFLICT',
        downloadUrl: undefined,
        fileName: '账本报告.pdf',
        status: 'expired',
      }),
    ]);
  });

  it('projects history records to chat timeline items', () => {
    const items = recordsToTimelineItems([
      {
        id: '1',
        senderRole: 'user',
        contentText: '帮我查一下',
        roundId: 'r1',
        contentPayload: {
          attachments: [
            {
              attachmentRef: 'formal:r1:document:0',
              downloadUrl:
                '/api/v1/agent/media/document/content?reference=formal:r1:document:0',
              name: '需求说明.docx',
              readSnapshotId: 'snapshot-1',
              size: 1024,
            },
          ],
        },
      },
      {
        id: '2',
        senderRole: 'assistant',
        contentText: '查到了',
        roundId: 'r1',
        contentPayload: {
          agentResult: {
            reasoning: '先检索',
            text: '查到了',
            knowledgeReferences: [
              {
                documentName: '手册',
                chunkContent: '内容片段',
              },
            ],
          },
          chains: [
            {
              id: 'tool-1',
              name: 'search',
              status: 'TOOL_RESULT',
              arguments: { q: 'EasyFlow' },
              result: 'ok',
            },
          ],
        },
      },
    ]);

    expect(
      items.some((item) => item.type === 'message' && item.role === 'user'),
    ).toBe(true);
    const user = items.find(
      (item): item is ChatTimelineMessageItem =>
        item.type === 'message' && item.role === 'user',
    );
    expect(user?.documents?.[0]).toEqual(
      expect.objectContaining({
        attachmentRef: 'formal:r1:document:0',
        name: '需求说明.docx',
        status: 'ready',
      }),
    );
    const assistant = items.find(
      (item): item is ChatTimelineMessageItem =>
        item.type === 'message' && item.role === 'assistant',
    );
    expect(assistant?.parts.some((part) => part.type === 'thinking')).toBe(
      true,
    );
    expect(assistant?.parts.some((part) => part.type === 'text')).toBe(true);
    expect(items.some((item) => item.type === 'tool')).toBe(true);
    expect(assistant?.knowledgeItems?.[0]?.documentName).toBe('手册');
    expect(
      items
        .filter((item) => item.type !== 'message' || item.role !== 'user')
        .every((item) => item.roundId === 'r1' && item.turnSucceeded === true),
    ).toBe(true);
  });

  it('keeps a cancelled partial assistant turn expanded after history restore', () => {
    const items = recordsToTimelineItems([
      {
        id: 'cancelled-assistant',
        senderRole: 'assistant',
        contentText: '取消前的部分输出',
        created: '2026-08-15T10:00:00Z',
        roundId: 'cancelled-round',
        contentPayload: {
          agentResult: { text: '取消前的部分输出' },
          terminalStatus: 'CANCELLED',
        },
      },
    ]);

    expect(
      items
        .filter((item) => item.roundId === 'cancelled-round')
        .every(
          (item) =>
            item.turnSucceeded === false && item.turnFinishedAt !== undefined,
        ),
    ).toBe(true);
  });

  it('keeps stable ids when history has reasoning, tools and final text', () => {
    const items = recordsToTimelineItems([
      {
        id: '42',
        senderRole: 'assistant',
        contentText: '最终回答',
        roundId: 'r2',
        contentPayload: {
          agentResult: {
            text: '最终回答',
          },
          chains: [
            {
              reasoning_content: '先思考',
            },
            {
              id: 'tool-2',
              name: 'search',
              status: 'TOOL_RESULT',
              result: 'ok',
            },
          ],
          messageChain: [
            {
              role: 'assistant',
              reasoningContent: '中间思考',
              toolCalls: [{ id: 'tool-2', name: 'search', arguments: '{}' }],
            },
            {
              role: 'tool',
              toolCallId: 'tool-2',
              content: 'ok',
            },
          ],
        },
      },
    ]);

    const ids = items.map((item) => item.id);
    expect(new Set(ids).size).toBe(ids.length);
    expect(
      items.filter(
        (item) => item.type === 'message' && item.role === 'assistant',
      ),
    ).toHaveLength(2);
    expect(
      items.some((item) => item.type === 'tool' && item.status === 'success'),
    ).toBe(true);
  });

  it('uses tool display name when restoring aliased MCP tools from history', () => {
    const items = recordsToTimelineItems([
      {
        id: 'mcp-history',
        senderRole: 'assistant',
        contentText: '已完成',
        roundId: 'round-mcp',
        contentPayload: {
          chains: [
            {
              id: 'tool-mcp-1',
              name: 'mcp_123_search',
              toolDisplayName: 'Context MCP - search',
              status: 'TOOL_RESULT',
              result: 'ok',
            },
          ],
          messageChain: [
            {
              role: 'assistant',
              toolCalls: [
                {
                  id: 'tool-mcp-1',
                  name: 'mcp_123_search',
                  toolDisplayName: 'Context MCP - search',
                  arguments: '{}',
                },
              ],
            },
            {
              role: 'tool',
              toolCallId: 'tool-mcp-1',
              content: 'ok',
            },
          ],
        },
      },
    ]);

    const tool = items.find((item) => item.type === 'tool');
    expect(tool?.toolName).toBe('Context MCP - search');
  });

  it('hides knowledge retrieval cards when restoring agent chat history', () => {
    const items = recordsToTimelineItems([
      {
        id: '417197643647811584',
        senderRole: 'assistant',
        contentText:
          '根据知识库中的信息，2026年暑假的时间安排是：7月1日到8月15日。',
        roundId: '417197622424633344',
        contentPayload: {
          chains: [
            {
              id: 'call_0fc660e9d203416983ccca7e',
              name: 'retrieve_knowledge',
              result: 'Retrieved 2 relevant document(s)',
              status: 'TOOL_RESULT',
              arguments: {
                query: '暑假时间',
              },
            },
          ],
          agentResult: {
            text: '根据知识库中的信息，2026年暑假的时间安排是：7月1日到8月15日。',
            knowledgeReferences: [
              {
                chunkContent:
                  '问题：2026 年暑假安排\n答案：2026 年7 月 1 日到 8 月 15 日',
                documentId: '411358369563336704',
                knowledgeName: 'faq',
                knowledgeType: 'FAQ',
              },
            ],
          },
          messageChain: [
            {
              role: 'assistant',
              toolCalls: [
                {
                  id: 'call_0fc660e9d203416983ccca7e',
                  name: 'retrieve_knowledge',
                  arguments: '{query=暑假时间}',
                },
              ],
            },
            {
              role: 'tool',
              content: 'Retrieved 2 relevant document(s)',
              toolCallId: 'call_0fc660e9d203416983ccca7e',
            },
            {
              role: 'assistant',
              content:
                '根据知识库中的信息，2026年暑假的时间安排是：7月1日到8月15日。',
            },
          ],
        },
      },
    ]);

    expect(items.some((item) => item.type === 'tool')).toBe(false);
    expect(
      items.some(
        (item) => item.type === 'status' && item.label === '已检索知识库',
      ),
    ).toBe(true);
    expect(
      items.some(
        (item) => item.type === 'message' && item.role === 'assistant',
      ),
    ).toBe(true);
    const assistant = items.find(
      (item): item is ChatTimelineMessageItem =>
        item.type === 'message' && item.role === 'assistant',
    );
    expect(assistant?.knowledgeItems?.[0]?.knowledgeName).toBe('faq');
  });

  it('hides internal fragment and context reload tools from history', () => {
    const items = recordsToTimelineItems([
      {
        id: 'internal-tools',
        senderRole: 'assistant',
        contentText: '已处理',
        roundId: 'round-internal',
        contentPayload: {
          chains: [
            {
              id: 'fragment-1',
              name: '__fragment__',
              status: 'TOOL_RESULT',
              result: 'fragment',
            },
            {
              id: 'context-1',
              name: 'context_reload',
              status: 'TOOL_RESULT',
              result: 'reload',
            },
          ],
          agentResult: {
            text: '已处理',
          },
          messageChain: [
            {
              role: 'assistant',
              toolCalls: [
                { id: 'fragment-1', name: '__fragment__' },
                { id: 'context-1', name: 'context_reload' },
              ],
            },
            {
              role: 'tool',
              toolCallId: 'fragment-1',
              content: 'fragment',
            },
            {
              role: 'tool',
              toolCallId: 'context-1',
              content: 'reload',
            },
          ],
        },
      },
    ]);

    expect(items.some((item) => item.type === 'tool')).toBe(false);
    expect(items.some((item) => item.type === 'status')).toBe(false);
    expect(
      items.some(
        (item) => item.type === 'message' && item.role === 'assistant',
      ),
    ).toBe(true);
  });

  it('restores only terminal Skill invocation states from safe history fields', () => {
    const items = recordsToTimelineItems([
      {
        id: 'skill-history',
        senderRole: 'assistant',
        contentText: '处理结束',
        roundId: 'round-skill-history',
        contentPayload: {
          agentResult: { text: '处理结束' },
          skillInvocationStatuses: [
            {
              input: { private: true },
              path: 'references/private.md',
              skillContent: 'private body',
              skillDisplayName: '不会恢复的运行态',
              status: 'RUNNING',
              statusKey: 'skill-invocation:round-skill-history:running',
            },
            {
              configJson: { token: 'secret' },
              skillDisplayName: '合同审查助手',
              status: 'SUCCESS',
              statusKey: 'skill-invocation:round-skill-history:101',
            },
            {
              skillDisplayName: '数据分析助手',
              status: 'FAILED',
              statusKey: 'skill-invocation:round-skill-history:102',
            },
            {
              skillDisplayName: '流程检查助手',
              status: 'CANCELLED',
              statusKey: 'skill-invocation:round-skill-history:103',
            },
            {
              skillDisplayName: '规范化助手',
              status: 'INCOMPLETE',
              statusKey: 'skill-invocation:round-skill-history:104',
            },
          ],
        },
      },
    ]);

    const skillStatuses = items.filter(
      (item): item is ChatTimelineStatusItem =>
        item.type === 'status' && item.icon === 'skill',
    );
    expect(skillStatuses).toHaveLength(4);
    expect(skillStatuses.map((item) => item.status)).toEqual([
      'done',
      'error',
      'cancelled',
      'incomplete',
    ]);
    expect(JSON.stringify(skillStatuses)).not.toMatch(
      /不会恢复的运行态|skillContent|private body|private\.md|configJson|token/,
    );
  });
});
