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

import {
  finalizeWorkflowExecutionSteps,
  formatExecutionValue,
  hydrateWorkflowExecutionSteps,
  reduceWorkflowExecutionSteps,
} from './workflowExecutionDetails';

describe('workflowExecutionDetails', () => {
  it('keeps loop attempts separate and completes each output', () => {
    const first = reduceWorkflowExecutionSteps([], {
      data: {
        attemptKey: 'loop:1',
        input: { index: 0 },
        nodeId: 'llm',
        nodeName: '大模型',
        startedAt: 100,
      },
      eventId: '1',
      type: 'node_started',
    });
    const firstDone = reduceWorkflowExecutionSteps(
      first,
      {
        data: {
          attemptKey: 'loop:1',
          finishedAt: 150,
          nodeId: 'llm',
          nodeName: '大模型',
          output: { text: '第一轮完整输出' },
          status: 'SUCCEEDED',
        },
        eventId: '2',
        type: 'node_finished',
      },
      150,
    );
    const second = reduceWorkflowExecutionSteps(firstDone, {
      data: {
        attemptKey: 'loop:2',
        input: { index: 1 },
        nodeId: 'llm',
        nodeName: '大模型',
        startedAt: 200,
      },
      eventId: '3',
      type: 'node_started',
    });

    expect(second).toHaveLength(2);
    expect(second[0]).toMatchObject({
      duration: 50,
      output: { text: '第一轮完整输出' },
      status: 'completed',
    });
    expect(second[1]).toMatchObject({
      attemptKey: 'loop:2',
      status: 'running',
    });
  });

  it('appends condition decisions to the source attempt', () => {
    const started = reduceWorkflowExecutionSteps([], {
      data: {
        attemptKey: 'condition:1',
        nodeId: 'condition',
        nodeName: '条件判断',
      },
      eventId: '1',
      type: 'node_started',
    });
    const traced = reduceWorkflowExecutionSteps(started, {
      data: {
        attemptKey: 'condition:1',
        nodeId: 'condition',
        outcome: 'skipped',
        targetNodeName: '拒绝分支',
      },
      eventId: '2',
      type: 'node_trace',
    });

    expect(traced[0]?.traces).toEqual([
      {
        id: '2',
        outcome: 'skipped',
        targetNodeName: '拒绝分支',
      },
    ]);
  });

  it('hydrates persisted JSON values and finalizes active steps', () => {
    const hydrated = hydrateWorkflowExecutionSteps([
      {
        attemptKey: 'node:1',
        input: '{"question":"你好"}',
        nodeId: 'node',
        nodeName: '节点',
        output: '{"answer":"你好"}',
        status: 20,
      },
    ]);
    const running = reduceWorkflowExecutionSteps(hydrated, {
      data: {
        attemptKey: 'node:2',
        nodeId: 'node',
        nodeName: '节点',
        startedAt: 100,
      },
      eventId: '2',
      type: 'node_started',
    });
    const finalized = finalizeWorkflowExecutionSteps(running, 'cancelled', 160);

    expect(finalized[0]).toMatchObject({
      input: { question: '你好' },
      output: { answer: '你好' },
    });
    expect(finalized[1]).toMatchObject({
      duration: 60,
      status: 'cancelled',
    });
  });

  it('filters thinking blocks only when formatting node outputs', () => {
    const value = {
      text: '<thinking>内部推理</thinking>最终答案',
    };

    expect(formatExecutionValue(value)).toContain('内部推理');
    expect(formatExecutionValue(value, true)).toContain('最终答案');
    expect(formatExecutionValue(value, true)).not.toContain('内部推理');
  });
});
