import type { Node } from '@xyflow/svelte';
import { describe, expect, it } from 'vitest';

import {
  buildLoopItemParameter,
  buildLoopReferenceParameters,
  buildLoopScopeParameters,
  findReferenceParameter,
  resolveLoopOutputDataType,
  resolveLoopInputs,
} from './loopScope';

const knowledgeNode = {
  id: 'knowledge',
  type: 'knowledgeNode',
  position: { x: 0, y: 0 },
  data: {
    outputDefs: [
      {
        name: 'documents',
        dataType: 'Array',
        children: [
          { name: 'title', dataType: 'String' },
          { name: 'content', dataType: 'String' },
          { name: 'documentId', dataType: 'Number' },
          { name: 'knowledgeId', dataType: 'Number' },
        ],
      },
    ],
  },
} satisfies Node;

describe('loopScope', () => {
  it('同时解析次数和对象数组，并生成对象 item 字段', () => {
    const loopNode = {
      id: 'loop',
      type: 'loopNode',
      position: { x: 0, y: 0 },
      data: {
        loopInputs: {
          count: {
            name: 'count',
            refType: 'fixed',
            value: '2',
            dataType: 'Number',
          },
          items: {
            name: 'items',
            refType: 'ref',
            ref: 'knowledge.documents',
            dataType: 'Array',
          },
        },
      },
    } satisfies Node;

    const inputs = resolveLoopInputs(
      loopNode,
      [knowledgeNode, loopNode],
    );
    const loopItem = buildLoopItemParameter(
      loopNode,
      [knowledgeNode, loopNode],
    );

    expect(inputs.count?.value).toBe('2');
    expect(inputs.items?.ref).toBe('knowledge.documents');
    expect(loopItem?.dataType).toBe('Object');
    expect(loopItem?.children?.map((item) => item.name)).toEqual([
      'title',
      'content',
      'documentId',
      'knowledgeId',
    ]);
  });

  it('仅在对应循环体中暴露 index 和 loopItem', () => {
    const loopNode = {
      id: 'loop',
      type: 'loopNode',
      position: { x: 0, y: 0 },
      data: {
        loopInputs: {
          items: {
            name: 'items',
            refType: 'ref',
            ref: 'knowledge.documents',
            dataType: 'Array',
          },
        },
      },
    } satisfies Node;
    const childNode = {
      id: 'child',
      type: 'llmNode',
      parentId: 'loop',
      position: { x: 0, y: 0 },
      data: {},
    } satisfies Node;
    const outsideNode = {
      id: 'outside',
      type: 'llmNode',
      position: { x: 0, y: 0 },
      data: {},
    } satisfies Node;
    const nodes = [knowledgeNode, loopNode, childNode, outsideNode];

    expect(
      buildLoopScopeParameters(loopNode, childNode, nodes)
        .map((item) => item.name),
    ).toEqual(['index', 'loopItem']);
    expect(
      buildLoopScopeParameters(loopNode, outsideNode, nodes),
    ).toEqual([]);
  });

  it('兼容旧版数组 loopVars', () => {
    const loopNode = {
      id: 'loop',
      type: 'loopNode',
      position: { x: 0, y: 0 },
      data: {
        loopVars: [
          {
            name: 'loopVar',
            refType: 'ref',
            ref: 'knowledge.documents',
            dataType: 'Array',
          },
        ],
      },
    } satisfies Node;

    const inputs = resolveLoopInputs(
      loopNode,
      [knowledgeNode, loopNode],
    );

    expect(inputs.legacy).toBe(true);
    expect(inputs.items?.name).toBe('items');
    expect(inputs.count).toBeUndefined();
  });

  it('纯次数循环把 loopItem 声明为当前数值序号', () => {
    const loopNode = {
      id: 'loop',
      type: 'loopNode',
      position: { x: 0, y: 0 },
      data: {
        loopInputs: {
          count: {
            name: 'count',
            refType: 'fixed',
            value: '3',
            dataType: 'Number',
          },
        },
      },
    } satisfies Node;

    expect(
      buildLoopItemParameter(loopNode, [loopNode])?.dataType,
    ).toBe('Number');
  });

  it('循环体内只暴露临时变量，下游只暴露正式输出', () => {
    const loopNode = {
      id: 'loop',
      type: 'loopNode',
      position: { x: 0, y: 0 },
      data: {
        loopInputs: {
          items: {
            name: 'items',
            refType: 'ref',
            ref: 'knowledge.documents',
            dataType: 'Array',
          },
        },
        outputDefs: [
          {
            name: 'res',
            ref: 'child.output',
            dataType: 'String',
          },
          {
            name: 'records',
            ref: 'child.record',
            dataType: 'String',
          },
        ],
      },
    } satisfies Node;
    const childNode = {
      id: 'child',
      type: 'llmNode',
      parentId: 'loop',
      position: { x: 0, y: 0 },
      data: {
        outputDefs: [
          { name: 'output', dataType: 'String' },
          {
            name: 'record',
            dataType: 'Object',
            children: [{ name: 'summary', dataType: 'String' }],
          },
        ],
      },
    } satisfies Node;
    const downstreamNode = {
      id: 'downstream',
      type: 'endNode',
      position: { x: 0, y: 0 },
      data: {},
    } satisfies Node;
    const nodes = [knowledgeNode, loopNode, childNode, downstreamNode];

    expect(
      buildLoopReferenceParameters(loopNode, childNode, nodes)
        .map((item) => item.name),
    ).toEqual(['index', 'loopItem']);
    const downstreamParameters = buildLoopReferenceParameters(
      loopNode,
      downstreamNode,
      nodes,
    );
    expect(downstreamParameters.map((item) => item.name)).toEqual([
      'res',
      'records',
    ]);
    expect(downstreamParameters[0]?.dataType).toBe('Array<String>');
    expect(downstreamParameters[1]?.dataType).toBe('Array<Object>');
    expect(downstreamParameters[1]?.children?.[0]?.name).toBe('summary');

    downstreamParameters[1]!.children![0]!.name = 'changed';
    expect(
      (childNode.data.outputDefs as Array<any>)[1].children[0].name,
    ).toBe('summary');
  });

  it('集合子字段按实际层级推导循环输出类型', () => {
    const loopNode = {
      id: 'loop',
      type: 'loopNode',
      position: { x: 0, y: 0 },
      data: {
        loopInputs: {
          count: {
            name: 'count',
            refType: 'fixed',
            value: '2',
            dataType: 'Number',
          },
        },
        outputDefs: [
          {
            name: 'grouped',
            refType: 'ref',
            ref: 'knowledge.documents.content',
            dataType: 'Array<String>',
          },
          {
            name: 'flattened',
            refType: 'ref',
            ref: 'knowledge.documents.content',
            dataType: 'Array<String>',
            flattenAggregation: true,
          },
        ],
      },
    } satisfies Node;
    const downstreamNode = {
      id: 'downstream',
      type: 'endNode',
      position: { x: 0, y: 0 },
      data: {},
    } satisfies Node;
    const nodes = [knowledgeNode, loopNode, downstreamNode];

    expect(
      findReferenceParameter(
        nodes,
        'knowledge.documents.content',
      )?.dataType,
    ).toBe('Array<String>');
    expect(
      buildLoopReferenceParameters(loopNode, downstreamNode, nodes)
        .map((parameter) => parameter.dataType),
    ).toEqual(['Array<Array<String>>', 'Array<String>']);
  });

  it('扁平聚合只移除循环新增的一层数组', () => {
    expect(resolveLoopOutputDataType('String', false))
      .toBe('Array<String>');
    expect(resolveLoopOutputDataType('Array<String>', false))
      .toBe('Array<Array<String>>');
    expect(resolveLoopOutputDataType('Array<String>', true))
      .toBe('Array<String>');
  });
});
