/**
 * Unit tests for tool-name-sanitizer module
 */

const assert = require('assert');

const {
  isValidToolName,
  removeDuplicateSegments,
  smartTruncate,
  sanitizeToolName,
  GEMINI_MAX_TOOL_NAME_LENGTH,
} = require('../../../dist/cliproxy/tool-name-sanitizer');

describe('Tool Name Sanitizer', () => {
  describe('GEMINI_MAX_TOOL_NAME_LENGTH', () => {
    it('should be 64', () => {
      assert.strictEqual(GEMINI_MAX_TOOL_NAME_LENGTH, 64);
    });
  });

  describe('isValidToolName', () => {
    it('returns true for valid short names', () => {
      assert.strictEqual(isValidToolName('my_tool'), true);
      assert.strictEqual(isValidToolName('a'), true);
      assert.strictEqual(isValidToolName('_underscore'), true);
      assert.strictEqual(isValidToolName('Tool123'), true);
    });

    it('returns true for names with valid special characters', () => {
      assert.strictEqual(isValidToolName('my.tool'), true);
      assert.strictEqual(isValidToolName('my:tool'), true);
      assert.strictEqual(isValidToolName('my-tool'), true);
      assert.strictEqual(isValidToolName('my_tool.v1:test-123'), true);
    });

    it('returns true for exactly 64 character names', () => {
      const name64 = 'a'.repeat(64);
      assert.strictEqual(isValidToolName(name64), true);
    });

    it('returns false for names exceeding 64 characters', () => {
      const name65 = 'a'.repeat(65);
      assert.strictEqual(isValidToolName(name65), false);
    });

    it('returns false for empty string', () => {
      assert.strictEqual(isValidToolName(''), false);
    });

    it('returns false for names starting with number', () => {
      assert.strictEqual(isValidToolName('123start'), false);
      assert.strictEqual(isValidToolName('0tool'), false);
    });

    it('returns false for names with invalid characters', () => {
      assert.strictEqual(isValidToolName('has space'), false);
      assert.strictEqual(isValidToolName('has@symbol'), false);
      assert.strictEqual(isValidToolName('has#hash'), false);
    });
  });

  describe('removeDuplicateSegments', () => {
    it('removes consecutive duplicate segments', () => {
      assert.strictEqual(removeDuplicateSegments('foo__bar__bar'), 'foo__bar');
      assert.strictEqual(removeDuplicateSegments('gitmcp__x__x'), 'gitmcp__x');
    });

    it('removes multiple consecutive duplicates', () => {
      assert.strictEqual(removeDuplicateSegments('a__b__b__b'), 'a__b');
    });

    it('handles non-consecutive duplicates (keeps them)', () => {
      // Only consecutive duplicates are removed
      assert.strictEqual(removeDuplicateSegments('a__b__a'), 'a__b__a');
    });

    it('returns unchanged when no duplicates', () => {
      assert.strictEqual(removeDuplicateSegments('foo__bar__baz'), 'foo__bar__baz');
      assert.strictEqual(removeDuplicateSegments('no_dupes'), 'no_dupes');
    });

    it('handles single segment', () => {
      assert.strictEqual(removeDuplicateSegments('single'), 'single');
    });

    it('handles real-world MCP tool name with duplicates', () => {
      const input = 'gitmcp__plus-pro-components__plus-pro-components';
      const expected = 'gitmcp__plus-pro-components';
      assert.strictEqual(removeDuplicateSegments(input), expected);
    });
  });

  describe('smartTruncate', () => {
    it('returns unchanged for short names', () => {
      assert.strictEqual(smartTruncate('short', 64), 'short');
      assert.strictEqual(smartTruncate('a'.repeat(64), 64), 'a'.repeat(64));
    });

    it('truncates long names with hash suffix', () => {
      const longName = 'a'.repeat(100);
      const result = smartTruncate(longName, 64);

      assert.strictEqual(result.length, 64);
      assert.ok(result.includes('_')); // Has underscore separator
    });

    it('produces deterministic output', () => {
      const name = 'a'.repeat(100);
      const result1 = smartTruncate(name, 64);
      const result2 = smartTruncate(name, 64);

      assert.strictEqual(result1, result2);
    });

    it('produces different hashes for different inputs', () => {
      const result1 = smartTruncate('a'.repeat(100), 64);
      const result2 = smartTruncate('b'.repeat(100), 64);

      assert.notStrictEqual(result1, result2);
    });

    it('respects custom maxLen', () => {
      const result = smartTruncate('a'.repeat(50), 30);
      assert.strictEqual(result.length, 30);
    });
  });

  describe('sanitizeToolName', () => {
    it('returns unchanged for valid names without duplicates', () => {
      const result = sanitizeToolName('valid_name');
      assert.deepStrictEqual(result, { sanitized: 'valid_name', changed: false });
    });

    it('returns unchanged for 64-char valid names without duplicates', () => {
      const name64 = 'a'.repeat(64);
      const result = sanitizeToolName(name64);
      assert.deepStrictEqual(result, { sanitized: name64, changed: false });
    });

    it('removes duplicate segments', () => {
      const result = sanitizeToolName('foo__bar__bar');
      assert.strictEqual(result.sanitized, 'foo__bar');
      assert.strictEqual(result.changed, true);
    });

    it('truncates names exceeding 64 chars', () => {
      const longName = 'a'.repeat(100);
      const result = sanitizeToolName(longName);

      assert.strictEqual(result.sanitized.length, 64);
      assert.strictEqual(result.changed, true);
    });

    it('handles combined dedupe + truncate', () => {
      // Create a name that needs both dedupe and truncate
      const segment = 'very_long_segment_name_here';
      const duplicated = `prefix__${segment}__${segment}__${segment}`;
      const result = sanitizeToolName(duplicated);

      assert.ok(result.sanitized.length <= 64);
      assert.strictEqual(result.changed, true);
    });

    it('sanitizes real-world problematic MCP tool name', () => {
      const problematic = 'gitmcp__plus-pro-components__plus-pro-components';
      const result = sanitizeToolName(problematic);

      assert.ok(result.sanitized.length <= 64);
      assert.strictEqual(result.changed, true);
      assert.strictEqual(result.sanitized, 'gitmcp__plus-pro-components');
    });
  });
});
