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

import {
  buildWorkflowFinalOutputView,
  formatWorkflowOutputPreview,
  sanitizeWorkflowOutputForDisplay,
  serializeWorkflowOutput,
} from './workflowFinalOutput';

describe('workflowFinalOutput', () => {
  it('keeps output parameter names and classifies scalar fields', () => {
    const view = buildWorkflowFinalOutputView({
      answer: '你好',
      count: 2,
      enabled: true,
      nullable: null,
    });

    expect(view.sections.map((section) => section.label)).toEqual([
      'answer',
      'count',
      'enabled',
      'nullable',
    ]);
    expect(view.sections.map((section) => section.kind)).toEqual([
      'scalar',
      'scalar',
      'scalar',
      'scalar',
    ]);
    expect(view.sections[3]?.scalarText).toBe('空值');
  });

  it('classifies primitive arrays and homogeneous object arrays', () => {
    const view = buildWorkflowFinalOutputView({
      labels: ['A', 'B'],
      rows: [
        { name: 'A', score: 90 },
        { name: 'B', score: 85 },
      ],
    });

    expect(view.sections[0]).toMatchObject({
      kind: 'list',
      listItems: ['A', 'B'],
    });
    expect(view.sections[1]).toMatchObject({
      kind: 'table',
      tableColumns: ['name', 'score'],
    });
  });

  it('uses key-value and bounded JSON views for objects', () => {
    const view = buildWorkflowFinalOutputView({
      metadata: { id: 1, ok: true },
      nested: { page: { items: [{ id: 1 }] } },
    });

    expect(view.sections[0]?.kind).toBe('key-value');
    expect(view.sections[1]?.kind).toBe('json');
    expect(
      formatWorkflowOutputPreview(
        Array.from({ length: 20 }, (_, index) => index),
        false,
      ),
    ).toContain('另有 8 项');
  });

  it('separates image and document outputs from text bubbles', () => {
    const view = buildWorkflowFinalOutputView({
      image: {
        contentType: 'image/png',
        fileName: '<think>隐藏图片名</think>result.png',
        filePath: '/files/result.png',
      },
      report: {
        contentType: 'application/pdf',
        fileName: '<thinking>隐藏文件名</thinking>report.pdf',
        filePath: '/files/report.pdf',
      },
    });

    expect(view.sections[0]).toMatchObject({
      kind: 'images',
      images: [{ name: 'result.png', previewUrl: '/files/result.png' }],
    });
    expect(view.sections[1]).toMatchObject({
      documents: [{ downloadUrl: '/files/report.pdf', name: 'report.pdf' }],
      kind: 'documents',
    });
  });

  it('represents an empty result explicitly', () => {
    expect(buildWorkflowFinalOutputView({})).toEqual({
      empty: true,
      sections: [],
    });
    expect(buildWorkflowFinalOutputView(undefined)).toEqual({
      empty: true,
      sections: [],
    });
  });

  it('removes complete and unclosed thinking blocks from nested outputs', () => {
    const sanitized = sanitizeWorkflowOutputForDisplay({
      answer: '<think>内部推理</think>最终答案',
      items: ['前缀<THINKING>隐藏内容</THINKING>后缀', '<think>未闭合内容'],
      nested: {
        text: '可见内容</think>',
      },
    });

    expect(sanitized).toEqual({
      answer: '最终答案',
      items: ['前缀后缀', ''],
      nested: {
        text: '可见内容',
      },
    });
  });

  it('removes nested and mixed thinking blocks without exposing outer content', () => {
    const sanitized = sanitizeWorkflowOutputForDisplay({
      mixed: '<think>外层<thinking>内层</thinking>仍属外层</think>最终答案',
      same: '前缀<think>第一层<think>第二层</think>第一层结尾</think>后缀',
      unclosed: '<THINKING>隐藏<think>继续隐藏</think>仍未闭合',
    });

    expect(sanitized).toEqual({
      mixed: '最终答案',
      same: '前缀后缀',
      unclosed: '',
    });
  });

  it('uses sanitized values for rendering previews and copy text', () => {
    const output = {
      answer: '<think>\n推理过程\n</think>\n最终答案',
    };
    const view = buildWorkflowFinalOutputView(output);

    expect(view.sections[0]?.scalarText).toBe('\n最终答案');
    expect(formatWorkflowOutputPreview(output)).not.toContain('推理过程');
    expect(serializeWorkflowOutput(output)).not.toContain('推理过程');
    expect(serializeWorkflowOutput(output)).not.toContain('<think>');
  });

  it('sanitizes only values reached by the bounded preview', () => {
    const nested: Record<string, unknown> = {
      visible: '<think>隐藏内容</think>展示内容',
    };
    for (let index = 1; index < 20; index += 1) {
      Object.defineProperty(nested, `field${index}`, {
        enumerable: true,
        get() {
          if (index >= 16) {
            throw new Error('不应访问预览范围外的数据');
          }
          return index;
        },
      });
    }

    const preview = formatWorkflowOutputPreview(nested);

    expect(preview).toContain('展示内容');
    expect(preview).not.toContain('隐藏内容');
    expect(preview).toContain('另有 4 个字段');
  });
});
