import type { ChatTimelineItem } from '../types';

import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import ChatTimeline from '../ChatTimeline.vue';
import ChatTimelineTurn from '../ChatTimelineTurn.vue';

function completedTurnItems(): ChatTimelineItem[] {
  return [
    {
      id: 'reasoning-1',
      parts: [
        {
          content: '先检索资料',
          id: 'thinking-1',
          status: 'end',
          type: 'thinking',
        },
      ],
      role: 'assistant',
      roundCompleted: true,
      roundId: 'round-1',
      status: 'done',
      turnFinishedAt: 19_000,
      turnStartedAt: 1000,
      turnSucceeded: true,
      type: 'message',
    },
    {
      id: 'tool-1',
      input: { query: 'AG-UI' },
      mode: 'auto',
      roundCompleted: true,
      roundId: 'round-1',
      status: 'success',
      toolCallId: 'tool-1',
      toolName: 'Context7 查询',
      turnFinishedAt: 19_000,
      turnStartedAt: 1000,
      turnSucceeded: true,
      type: 'tool',
    },
    {
      id: 'assistant-final',
      parts: [
        { content: 'AG-UI 是智能体交互协议。', id: 'text-1', type: 'text' },
      ],
      role: 'assistant',
      roundCompleted: true,
      roundId: 'round-1',
      status: 'done',
      turnFinishedAt: 19_000,
      turnStartedAt: 1000,
      turnSucceeded: true,
      type: 'message',
    },
  ];
}

describe('chat timeline turn', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(19_000);
  });

  afterEach(() => {
    vi.clearAllTimers();
    vi.useRealTimers();
  });

  it('shows one running header for an empty RUN_STARTED placeholder', () => {
    const wrapper = mount(ChatTimeline, {
      props: {
        assistantAvatar: '/assistant.svg',
        items: [
          {
            id: 'turn-round-started',
            parts: [],
            role: 'assistant',
            roundId: 'round-started',
            status: 'streaming',
            turnStartedAt: 1000,
            type: 'message',
          },
        ],
      },
    });

    expect(wrapper.findAll('.chat-timeline-turn__avatar')).toHaveLength(1);
    expect(wrapper.find('.chat-timeline-turn__summary').text()).toBe(
      '已处理 18 秒',
    );
    expect(
      wrapper.findAll('.chat-timeline-item__assistant-avatar'),
    ).toHaveLength(0);
    expect(wrapper.text()).not.toContain('正在继续处理');
  });

  it('shows the running and completed states of an automatic tool call', async () => {
    const assistant: ChatTimelineItem = {
      id: 'assistant-fast-tool',
      parts: [
        {
          content: '开始生成文件。',
          id: 'text-fast-tool',
          type: 'text',
        },
      ],
      role: 'assistant',
      roundId: 'round-fast-tool',
      status: 'streaming',
      turnStartedAt: 1000,
      type: 'message',
    };
    const runningTool: ChatTimelineItem = {
      id: 'tool-fast-write',
      mode: 'auto',
      roundId: 'round-fast-tool',
      status: 'running',
      toolCallId: 'tool-fast-write',
      toolName: 'write_text_file',
      turnStartedAt: 1000,
      type: 'tool',
    };
    const wrapper = mount(ChatTimelineTurn, {
      props: {
        items: [assistant],
        roundId: 'round-fast-tool',
      },
    });

    expect(wrapper.text()).not.toContain('调用中');

    await wrapper.setProps({ items: [assistant, runningTool] });

    expect(wrapper.text()).toContain('调用中');

    await wrapper.setProps({
      items: [assistant, { ...runningTool, status: 'success' }],
    });

    expect(wrapper.text()).toContain('已完成');
    expect(wrapper.text()).not.toContain('正在继续处理');
  });

  it('updates the running duration every second and freezes it on success', async () => {
    vi.setSystemTime(1000);
    const runningItem: ChatTimelineItem = {
      id: 'turn-round-live',
      parts: [],
      role: 'assistant',
      roundId: 'round-live',
      status: 'streaming',
      turnStartedAt: 1000,
      type: 'message',
    };
    const wrapper = mount(ChatTimelineTurn, {
      props: {
        items: [runningItem],
        roundId: 'round-live',
      },
    });

    expect(wrapper.find('.chat-timeline-turn__summary').text()).toBe(
      '已处理 1 秒',
    );
    expect(vi.getTimerCount()).toBe(1);

    vi.advanceTimersByTime(64_000);
    await nextTick();

    expect(wrapper.find('.chat-timeline-turn__summary').text()).toBe(
      '已处理 1 分 4 秒',
    );

    await wrapper.setProps({
      items: [
        {
          ...runningItem,
          roundCompleted: true,
          status: 'done',
          turnFinishedAt: 65_000,
          turnSucceeded: true,
        },
      ],
    });

    expect(wrapper.find('.chat-timeline-turn__summary').text()).toBe(
      '已处理 1 分 4 秒',
    );
    expect(vi.getTimerCount()).toBe(0);

    vi.advanceTimersByTime(10_000);
    await nextTick();

    expect(wrapper.find('.chat-timeline-turn__summary').text()).toBe(
      '已处理 1 分 4 秒',
    );
  });

  it('switches the running timer to the incomplete terminal state', async () => {
    vi.setSystemTime(5000);
    const runningItem: ChatTimelineItem = {
      id: 'turn-round-error',
      parts: [],
      role: 'assistant',
      roundId: 'round-error',
      status: 'streaming',
      turnStartedAt: 1000,
      type: 'message',
    };
    const wrapper = mount(ChatTimelineTurn, {
      props: {
        items: [runningItem],
        roundId: 'round-error',
      },
    });

    expect(wrapper.find('.chat-timeline-turn__summary').text()).toBe(
      '已处理 4 秒',
    );

    await wrapper.setProps({
      items: [
        {
          ...runningItem,
          status: 'error',
          turnFinishedAt: 5000,
          turnSucceeded: false,
        },
      ],
    });

    expect(wrapper.find('.chat-timeline-turn__summary').text()).toBe(
      '处理未完成',
    );
    expect(vi.getTimerCount()).toBe(0);
  });

  it('keeps partial assistant text in event order while the turn is active', () => {
    const items: ChatTimelineItem[] = [
      {
        id: 'assistant-partial',
        parts: [
          {
            content: '先检查依赖',
            id: 'thinking-1',
            status: 'end',
            type: 'thinking',
          },
          { content: '正文 A', id: 'text-1', type: 'text' },
        ],
        role: 'assistant',
        roundId: 'round-live-order',
        status: 'streaming',
        turnStartedAt: 1000,
        type: 'message',
      },
      {
        id: 'tool-completed',
        mode: 'auto',
        roundId: 'round-live-order',
        status: 'success',
        toolCallId: 'tool-completed',
        toolName: '已执行工具',
        turnStartedAt: 1000,
        type: 'tool',
      },
      {
        id: 'assistant-reasoning',
        parts: [
          {
            content: '继续思考',
            id: 'thinking-2',
            status: 'thinking',
            type: 'thinking',
          },
        ],
        role: 'assistant',
        roundId: 'round-live-order',
        status: 'streaming',
        turnStartedAt: 1000,
        type: 'message',
      },
      {
        approval: {
          approvalId: 'approval-1',
          toolCallId: 'tool-pending',
          toolName: '待审批工具',
        },
        id: 'tool-pending',
        mode: 'approval',
        roundId: 'round-live-order',
        status: 'pending_approval',
        toolCallId: 'tool-pending',
        toolName: '待审批工具',
        turnStartedAt: 1000,
        type: 'tool',
      },
    ];
    const wrapper = mount(ChatTimelineTurn, {
      props: { items, roundId: 'round-live-order' },
    });
    const rendered = wrapper.text();

    expect(wrapper.find('[data-chat-turn-final]').exists()).toBe(false);
    expect(rendered.indexOf('正文 A')).toBeLessThan(
      rendered.indexOf('已执行工具'),
    );
    expect(rendered.indexOf('已执行工具')).toBeLessThan(
      rendered.indexOf('继续思考'),
    );
    expect(rendered.indexOf('继续思考')).toBeLessThan(
      rendered.indexOf('待审批工具'),
    );
  });

  it('renders one avatar and collapses process after a successful turn', async () => {
    const wrapper = mount(ChatTimeline, {
      props: {
        assistantAvatar: '/assistant.svg',
        items: completedTurnItems(),
      },
    });

    expect(wrapper.findAll('.chat-timeline-turn__avatar')).toHaveLength(1);
    expect(
      wrapper.findAll('.chat-timeline-item__assistant-avatar'),
    ).toHaveLength(0);
    expect(wrapper.find('.chat-timeline-turn__summary').text()).toContain(
      '已处理 18 秒',
    );
    expect(wrapper.text()).toContain('AG-UI 是智能体交互协议。');
    expect(wrapper.text()).not.toContain('Context7 查询');
    expect(wrapper.text()).not.toContain('先检索资料');

    await wrapper.find('.chat-timeline-turn__summary').trigger('click');

    expect(wrapper.text()).toContain('Context7 查询');
    expect(wrapper.text()).toContain('已思考');
    expect(wrapper.text()).toContain('AG-UI 是智能体交互协议。');
  });

  it('preserves the final-message scroll anchor while auto-collapsing', async () => {
    const runningItems = completedTurnItems().map((item) => {
      const {
        roundCompleted: _roundCompleted,
        turnFinishedAt: _turnFinishedAt,
        turnSucceeded: _turnSucceeded,
        ...runningItem
      } = item;
      return runningItem;
    });
    const wrapper = mount(ChatTimeline, {
      attachTo: document.body,
      global: { stubs: { Transition: false } },
      props: {
        assistantAvatar: '/assistant.svg',
        items: runningItems,
      },
    });
    const container = wrapper.find('.chat-timeline').element as HTMLElement;
    Object.defineProperties(container, {
      clientHeight: { configurable: true, value: 500 },
      scrollHeight: { configurable: true, value: 1200 },
      scrollTop: { configurable: true, value: 400, writable: true },
    });
    vi.spyOn(container, 'getBoundingClientRect').mockReturnValue({
      bottom: 500,
      top: 0,
    } as DOMRect);
    const header = wrapper.find('.chat-timeline-turn__header')
      .element as HTMLElement;
    vi.spyOn(header, 'getBoundingClientRect').mockReturnValue({
      bottom: -72,
      top: -100,
    } as DOMRect);
    const liveMessage = wrapper.find('[data-chat-turn-live-anchor]')
      .element as HTMLElement;
    vi.spyOn(liveMessage, 'getBoundingClientRect')
      .mockReturnValueOnce({ bottom: 700, top: 600 } as DOMRect)
      .mockReturnValue({ bottom: 400, top: 300 } as DOMRect);
    await wrapper.find('.chat-timeline').trigger('scroll');
    wrapper.findComponent(ChatTimelineTurn).vm.$emit('layoutToggle', 'round-1');

    await wrapper.setProps({ items: completedTurnItems() });
    await nextTick();
    wrapper.findComponent(ChatTimelineTurn).vm.$emit('layoutChanged');
    await nextTick();

    expect(container.scrollTop).toBe(100);
    wrapper.unmount();
  });

  it('keeps running and approval content expanded in one turn', () => {
    const items: ChatTimelineItem[] = [
      {
        id: 'reasoning-1',
        parts: [
          {
            content: '准备调用工具',
            id: 'thinking-1',
            status: 'thinking',
            type: 'thinking',
          },
        ],
        role: 'assistant',
        roundId: 'round-1',
        status: 'streaming',
        turnStartedAt: 1000,
        type: 'message',
      },
      {
        approval: {
          approvalId: 'approval-1',
          toolCallId: 'tool-1',
          toolName: 'context7',
        },
        id: 'tool-1',
        mode: 'approval',
        roundId: 'round-1',
        status: 'pending_approval',
        toolCallId: 'tool-1',
        toolName: 'Context7 查询',
        turnStartedAt: 1000,
        type: 'tool',
      },
    ];
    const wrapper = mount(ChatTimeline, {
      props: { assistantAvatar: '/assistant.svg', items },
    });

    expect(wrapper.findAll('.chat-timeline-turn__avatar')).toHaveLength(1);
    expect(wrapper.find('.chat-timeline-turn__summary').text()).toBe(
      '已处理 18 秒',
    );
    expect(wrapper.text()).toContain('准备调用工具');
    expect(wrapper.text()).toContain('Context7 查询');
    expect(
      wrapper.find('.chat-timeline-turn__summary').attributes('disabled'),
    ).toBeDefined();
  });

  it('renders one avatar for each of multiple turns', () => {
    const secondTurn = completedTurnItems().map((item) => ({
      ...item,
      id: `${item.id}-2`,
      roundId: 'round-2',
    }));
    const wrapper = mount(ChatTimeline, {
      props: {
        assistantAvatar: '/assistant.svg',
        items: [...completedTurnItems(), ...secondTurn],
      },
    });

    expect(wrapper.findAll('.chat-timeline-turn')).toHaveLength(2);
    expect(wrapper.findAll('.chat-timeline-turn__avatar')).toHaveLength(2);
  });
});
