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

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

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

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

function textMessage(
  role: 'assistant' | 'user',
  content: string,
  extra: Partial<Extract<ChatTimelineItem, { type: 'message' }>> = {},
): Extract<ChatTimelineItem, { type: 'message' }> {
  return {
    id: `${role}-${content}`,
    parts: [
      {
        id: `text-${content}`,
        content,
        type: 'text',
      },
    ],
    role,
    status: 'done',
    type: 'message',
    ...extra,
  };
}

describe('ChatTimeline toolbar', () => {
  afterEach(() => {
    vi.useRealTimers();
  });

  it('shows copy button for user messages and emits copy-message', async () => {
    const userMessage = textMessage('user', '用户问题');
    const wrapper = mount(ChatTimeline, {
      props: {
        copyable: () => true,
        items: [userMessage],
      },
    });

    const copyButton = wrapper.find('[aria-label="复制消息"]');
    expect(copyButton.exists()).toBe(true);

    await copyButton.trigger('click');

    expect(wrapper.emitted('copyMessage')?.[0]?.[0]).toEqual(userMessage);
  });

  it('shows copy and regenerate buttons for assistant messages', async () => {
    const assistantMessage = textMessage('assistant', '助手回答', {
      regenerable: true,
      roundId: 'round-1',
      roundCompleted: true,
    });
    const wrapper = mount(ChatTimeline, {
      props: {
        copyable: () => true,
        items: [assistantMessage],
        regenerable: () => true,
      },
    });

    expect(wrapper.find('[aria-label="复制消息"]').exists()).toBe(true);
    const regenerateButton = wrapper.find('[aria-label="重新生成"]');
    expect(regenerateButton.exists()).toBe(true);

    await regenerateButton.trigger('click');

    expect(wrapper.emitted('regenerateMessage')?.[0]?.[0]).toEqual(
      assistantMessage,
    );
  });

  it('shows a check icon after the copy action succeeds', async () => {
    vi.useFakeTimers();
    const copyAction = vi.fn().mockResolvedValue(true);
    const userMessage = textMessage('user', '用户问题');
    const wrapper = mount(ChatTimeline, {
      props: {
        copyAction,
        copyable: () => true,
        items: [userMessage],
      },
    });

    await wrapper.get('[aria-label="复制消息"]').trigger('click');
    await flushPromises();

    expect(copyAction).toHaveBeenCalledWith(userMessage);
    expect(wrapper.get('button').attributes('aria-label')).toBe('复制成功');
    expect(
      wrapper.find('.chat-message-toolbar__button.is-copied').exists(),
    ).toBe(false);
    expect(wrapper.emitted('copyMessage')).toBeUndefined();

    await vi.advanceTimersByTimeAsync(3000);
    expect(wrapper.get('button').attributes('aria-label')).toBe('复制消息');
  });

  it('renders variant navigator and disables boundary buttons', async () => {
    const assistantMessage = textMessage('assistant', '助手回答', {
      roundId: 'round-1',
      selectedVariantIndex: 1,
      roundCompleted: true,
      switchable: true,
      variantCount: 2,
      variantIndex: 1,
    });
    const wrapper = mount(ChatTimeline, {
      props: {
        copyable: () => true,
        items: [assistantMessage],
        regenerable: () => true,
      },
    });

    expect(wrapper.text()).toContain('1/2');
    const buttons = wrapper.findAll('.chat-variant-navigator__button');
    expect(buttons[0]?.attributes('disabled')).toBeDefined();
    expect(buttons[1]?.attributes('disabled')).toBeUndefined();

    await buttons[1]?.trigger('click');

    expect(wrapper.emitted('selectNextVariant')?.[0]?.[0]).toEqual(
      assistantMessage,
    );
  });

  it('disables regenerate while streaming or globally disabled', () => {
    const assistantMessage = textMessage('assistant', '助手回答', {
      roundId: 'round-1',
      status: 'streaming',
    });
    const wrapper = mount(ChatTimeline, {
      props: {
        copyable: () => true,
        items: [assistantMessage],
        regenerable: () => false,
        regenerateDisabled: true,
      },
    });

    expect(wrapper.find('[aria-label="重新生成"]').exists()).toBe(false);
  });

  it('does not show message toolbar before streaming completes', () => {
    const assistantMessage = textMessage('assistant', '助手回答', {
      roundId: 'round-1',
      selectedVariantIndex: 1,
      status: 'streaming',
      roundCompleted: false,
      switchable: true,
      variantCount: 2,
      variantIndex: 1,
    });
    const wrapper = mount(ChatTimeline, {
      props: {
        copyable: (item) => item.status === 'done',
        items: [assistantMessage],
        regenerable: (item) => item.status === 'done',
      },
    });

    expect(wrapper.find('[aria-label="复制消息"]').exists()).toBe(false);
    expect(wrapper.find('[aria-label="重新生成"]').exists()).toBe(false);
    expect(wrapper.find('.chat-variant-navigator').exists()).toBe(false);
  });

  it('keeps toolbar hidden for unfinished rounds even after a partial done segment', () => {
    const assistantMessage = textMessage('assistant', '助手回答', {
      roundId: 'round-1',
      status: 'done',
      roundCompleted: false,
      switchable: true,
      variantCount: 2,
      variantIndex: 1,
    });
    const wrapper = mount(ChatTimeline, {
      props: {
        copyable: () => false,
        items: [assistantMessage],
        regenerable: () => false,
      },
    });

    expect(wrapper.find('[aria-label="复制消息"]').exists()).toBe(false);
    expect(wrapper.find('[aria-label="重新生成"]').exists()).toBe(false);
  });

  it('only shows action toolbar on the last assistant segment of the round', () => {
    const assistantHead = textMessage('assistant', '前半段', {
      roundId: 'round-1',
      status: 'done',
      roundCompleted: true,
    });
    const assistantTail = textMessage('assistant', '后半段', {
      roundId: 'round-1',
      status: 'done',
      roundCompleted: true,
    });
    const wrapper = mount(ChatTimeline, {
      props: {
        copyable: () => true,
        items: [assistantHead, assistantTail],
        regenerable: () => true,
      },
    });

    const copyButtons = wrapper.findAll('[aria-label="复制消息"]');
    const regenerateButtons = wrapper.findAll('[aria-label="重新生成"]');

    expect(copyButtons).toHaveLength(1);
    expect(regenerateButtons).toHaveLength(1);
    expect(wrapper.text()).toContain('前半段');
    expect(wrapper.text()).toContain('后半段');
  });
});
