import { describe, expect, it } from 'vitest';
import {
  buildRunSegmentMessageIndices,
  findReplyMessageIndex,
  getPostTriggerSegmentMessages,
  getRunSegmentMessages,
  hasActiveStreamingReplyInRun,
  segmentHasFinalReply,
} from '@/pages/Chat/task-visualization';
import type { RawMessage } from '@/stores/chat';

describe('buildRunSegmentMessageIndices', () => {
  it('marks assistant messages between real user turns', () => {
    const messages: RawMessage[] = [
      { role: 'user', content: 'first question' },
      { role: 'assistant', content: [{ type: 'tool_use', id: 't1', name: 'exec', input: {} }] },
      { role: 'assistant', content: [{ type: 'tool_use', id: 't2', name: 'image', input: {} }] },
      { role: 'assistant', content: [{ type: 'text', text: 'done' }] },
      { role: 'user', content: 'follow up' },
      { role: 'assistant', content: [{ type: 'text', text: 'ok' }] },
    ];
    const nextUserMessageIndexes = [4, -1, -1, -1, -1, -1];
    const indices = buildRunSegmentMessageIndices(
      messages,
      nextUserMessageIndexes,
      (message) => message.role === 'user',
    );

    expect(indices.has(1)).toBe(true);
    expect(indices.has(2)).toBe(true);
    expect(indices.has(3)).toBe(true);
    expect(indices.has(5)).toBe(true);
    expect(indices.has(0)).toBe(false);
    expect(indices.has(4)).toBe(false);
  });

  it('folds leading assistant orphans before the first user in a paginated suffix', () => {
    const messages: RawMessage[] = [
      { role: 'assistant', content: [{ type: 'toolCall', id: 't1', name: 'exec', input: {} }] },
      { role: 'assistant', content: [{ type: 'toolCall', id: 't2', name: 'image', input: {} }] },
      { role: 'user', content: 'question fell off the earlier page' },
      { role: 'assistant', content: [{ type: 'text', text: 'done' }] },
    ];
    const nextUserMessageIndexes = [-1, -1, -1, -1];
    const indices = buildRunSegmentMessageIndices(
      messages,
      nextUserMessageIndexes,
      (message) => message.role === 'user',
    );

    expect(indices.has(0)).toBe(true);
    expect(indices.has(1)).toBe(true);
    expect(indices.has(3)).toBe(true);
    expect(indices.has(2)).toBe(false);
  });
});

describe('getPostTriggerSegmentMessages vs getRunSegmentMessages', () => {
  const isUser = (message: RawMessage) => message.role === 'user';

  it('keeps lifecycle segment empty while graph segment includes paginated orphans', () => {
    const messages: RawMessage[] = [
      { role: 'assistant', content: [{ type: 'text', text: 'prior answer' }] },
      { role: 'user', content: 'follow up' },
    ];

    expect(getPostTriggerSegmentMessages(messages, 1, -1)).toEqual([]);
    expect(getRunSegmentMessages(messages, 1, -1, isUser)).toEqual([
      { role: 'assistant', content: [{ type: 'text', text: 'prior answer' }] },
    ]);
  });

  it('does not attach a prior turn assistant when an earlier user exists in the window', () => {
    const messages: RawMessage[] = [
      { role: 'user', content: 'first' },
      { role: 'assistant', content: [{ type: 'text', text: 'first answer' }] },
      { role: 'user', content: 'second' },
    ];

    expect(getPostTriggerSegmentMessages(messages, 2, -1)).toEqual([]);
    expect(getRunSegmentMessages(messages, 2, -1, isUser)).toEqual([]);
  });
});

describe('segmentHasFinalReply', () => {
  it('returns true when a text reply follows all tool calls', () => {
    const segment: RawMessage[] = [
      { role: 'assistant', content: [{ type: 'text', text: 'Working on it.' }] },
      { role: 'assistant', content: [{ type: 'tool_use', id: 't1', name: 'exec', input: {} }] },
      { role: 'assistant', content: [{ type: 'text', text: '执行完成 ✅' }] },
    ];
    expect(segmentHasFinalReply(segment)).toBe(true);
  });

  it('returns false for narration before tools while the chain is still open', () => {
    const segment: RawMessage[] = [
      { role: 'assistant', content: [{ type: 'text', text: 'Let me fetch that.' }] },
      { role: 'assistant', content: [{ type: 'tool_use', id: 't1', name: 'exec', input: {} }] },
    ];
    expect(segmentHasFinalReply(segment)).toBe(false);
  });
});

describe('findReplyMessageIndex / hasActiveStreamingReplyInRun', () => {
  it('protects a history reply from fold when the run is open but not streaming', () => {
    const postTrigger: RawMessage[] = [
      { role: 'assistant', content: [{ type: 'text', text: '你好,我在。' }] },
    ];

    expect(hasActiveStreamingReplyInRun(true, false, null)).toBe(false);
    expect(findReplyMessageIndex(postTrigger, false)).toBe(0);
    expect(findReplyMessageIndex(postTrigger, true)).toBe(-1);
  });

  it('folds history when a stream bubble is active', () => {
    const postTrigger: RawMessage[] = [
      { role: 'assistant', content: [{ type: 'text', text: 'partial' }] },
    ];

    expect(hasActiveStreamingReplyInRun(true, true, null)).toBe(true);
    expect(findReplyMessageIndex(postTrigger, true)).toBe(-1);
  });
});