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

import {
  flattenParameterCandidates,
  findBackspaceTokenRange,
  findTokenRangeAtCursor,
  flattenParameterNames,
  insertTextAtCursor,
  parseTokenParts,
  splitTokenDisplay,
} from './paramToken';

describe('paramToken utils', () => {
  it('should flatten parameter names with nested paths', () => {
    const result = flattenParameterNames([
      {
        name: 'input',
      },
      {
        name: 'documents',
        children: [
          {
            name: 'title',
          },
          {
            name: 'meta',
            children: [
              {
                name: 'author',
              },
            ],
          },
        ],
      },
    ]);

    expect(result).toEqual([
      'input',
      'documents',
      'documents.title',
      'documents.meta',
      'documents.meta.author',
    ]);
  });

  it('should keep unresolved candidates in parameter list', () => {
    const result = flattenParameterCandidates([
      {
        name: 'input',
        refType: 'ref',
        ref: '',
      },
      {
        name: 'docs',
        refType: 'ref',
        ref: 'documents',
      },
      {
        name: 'runtimeInput',
        refType: 'input',
      },
    ]);

    expect(result).toEqual([
      {
        dataType: undefined,
        displayName: 'input',
        name: 'input',
        resolved: false,
      },
      {
        dataType: undefined,
        displayName: 'docs',
        name: 'docs',
        resolved: true,
      },
      {
        dataType: undefined,
        displayName: 'runtimeInput',
        name: 'runtimeInput',
        resolved: true,
      },
    ]);
  });

  it('should insert token text in the middle by cursor range', () => {
    const result = insertTextAtCursor('hello world', '{{input}}', 6, 11);
    expect(result).toEqual({
      value: 'hello {{input}}',
      cursor: 15,
    });
  });

  it('should append token text when cursor info is missing', () => {
    const result = insertTextAtCursor('hello', '{{name}}');
    expect(result).toEqual({
      value: 'hello{{name}}',
      cursor: 13,
    });
  });

  it('should parse token parts and mark valid tokens', () => {
    const parts = parseTokenParts('你好 {{ user.name }} 与 {{unknown}}', [
      'user.name',
      'docs',
    ]);

    expect(parts).toEqual([
      {
        type: 'text',
        text: '你好 ',
      },
      {
        type: 'token',
        text: '{{ user.name }}',
        key: 'user.name',
        valid: true,
      },
      {
        type: 'text',
        text: ' 与 ',
      },
      {
        type: 'token',
        text: '{{unknown}}',
        key: 'unknown',
        valid: false,
      },
    ]);
  });

  it('should keep plain text when token syntax is invalid', () => {
    const parts = parseTokenParts('abc {{}} def', ['a']);
    expect(parts).toEqual([
      {
        type: 'text',
        text: 'abc {{}} def',
      },
    ]);
  });

  it('should split token display and hide braces text', () => {
    const result = splitTokenDisplay('{{ user.name }}', 'user.name');
    expect(result).toEqual({
      hiddenPrefix: '{{ ',
      visibleText: 'user.name',
      hiddenSuffix: ' }}',
    });
  });

  it('should find full token range for backspace delete', () => {
    const content = 'hello {{input}} world';
    const tokenEndCursor = 'hello {{input}}'.length;
    const range = findBackspaceTokenRange(content, tokenEndCursor);

    expect(range).toEqual({
      start: 6,
      end: 15,
      text: '{{input}}',
      key: 'input',
    });
  });

  it('should support boundary match for arrow skip behavior', () => {
    const content = 'x{{docs}}y';
    const tokenStart = 1;
    const tokenEnd = 9;

    const rightBoundary = findTokenRangeAtCursor(content, tokenStart, {
      includeStart: true,
      includeEnd: false,
    });
    const leftBoundary = findTokenRangeAtCursor(content, tokenEnd, {
      includeStart: false,
      includeEnd: true,
    });

    expect(rightBoundary?.key).toBe('docs');
    expect(leftBoundary?.key).toBe('docs');
  });
});
