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

import {
  createBusinessCompletions,
  normalizeCodeEngine,
  resolveBusinessCompletionContext,
  shouldAutoAppendCallParens,
  shouldSkipBusinessCompletion,
} from './codeCompletion';

describe('codeCompletion utils', () => {
  it('should normalize engine aliases', () => {
    expect(normalizeCodeEngine('py')).toBe('python');
    expect(normalizeCodeEngine('python')).toBe('python');
    expect(normalizeCodeEngine('js')).toBe('javascript');
    expect(normalizeCodeEngine('javascript')).toBe('javascript');
    expect(normalizeCodeEngine('')).toBe('javascript');
  });

  it('should create business completion list with stable priority and dedupe', () => {
    const completions = createBusinessCompletions('python', [
      { name: 'input.text', resolved: true },
      { name: 'input.text', resolved: false },
      { name: 'question', resolved: false },
    ]);

    expect(completions[0]).toMatchObject({
      label: '_result',
      type: 'variable',
    });

    expect(
      completions.filter((item) => item.label === 'input.text').length,
    ).toBe(1);
    expect(
      completions.find((item) => item.label === 'input.text'),
    ).toMatchObject({
      apply: '{{input.text}}',
      detail: '参数模板',
    });
    expect(completions.find((item) => item.label === 'question')).toMatchObject(
      {
        apply: '{{question}}',
        detail: '参数模板（未映射）',
      },
    );

    expect(
      completions.some(
        (item) => item.type === 'snippet' && item.label === 'if-else',
      ),
    ).toBe(true);
  });

  it('should detect blocked nodes for comment/string/property contexts', () => {
    expect(shouldSkipBusinessCompletion('Comment')).toBe(true);
    expect(shouldSkipBusinessCompletion('LineComment')).toBe(true);
    expect(shouldSkipBusinessCompletion('String')).toBe(true);
    expect(shouldSkipBusinessCompletion('TemplateString')).toBe(true);
    expect(shouldSkipBusinessCompletion('PropertyName')).toBe(true);
    expect(shouldSkipBusinessCompletion('VariableName')).toBe(false);
  });

  it('should resolve template context for parameter token completion', () => {
    const source = 'value = {{input.use';
    const pos = source.length;
    const context = resolveBusinessCompletionContext(source, pos);
    expect(context).toBeTruthy();
    expect(context?.from).toBe(8);
    expect(context?.validFor).toBeInstanceOf(RegExp);
    expect(context?.validFor.source).toBe('^[\\w.]*$');
  });

  it('should resolve word context and skip property access context', () => {
    const source = '_result.value';
    expect(resolveBusinessCompletionContext(source, source.length)).toBeNull();

    const wordSource = 'ret';
    const context = resolveBusinessCompletionContext(
      wordSource,
      wordSource.length,
    );
    expect(context).toBeTruthy();
    expect(context?.from).toBe(0);
    expect(context?.validFor).toBeInstanceOf(RegExp);
    expect(context?.validFor.source).toBe('^[\\w$]*$');
  });

  it('should auto append call parens for function/method completions only', () => {
    expect(
      shouldAutoAppendCallParens({ label: 'print', type: 'function' }, ''),
    ).toBe(true);
    expect(
      shouldAutoAppendCallParens({ label: 'run', type: 'method' }, ''),
    ).toBe(true);
    expect(
      shouldAutoAppendCallParens({ label: 'count', type: 'variable' }, ''),
    ).toBe(false);
    expect(
      shouldAutoAppendCallParens({ label: 'print', type: 'function' }, '('),
    ).toBe(false);
    expect(
      shouldAutoAppendCallParens(
        { label: 'call', type: 'function', apply: 'call()' },
        '',
      ),
    ).toBe(false);
  });
});
