/**
 * Unit tests for the API-key placeholder detection helpers (shared by
 * apiKeyManager + the web UI).
 *
 * Locks the heuristic that prevents UI display strings ("lx_•••• …
 * (server-managed)" and the legacy "platform-managed" sentinel) from
 * leaking into persistence and overwriting real keys with garbage.
 */
import { describe, test, expect } from '@jest/globals';
import {
  isPlaceholderApiKey,
  sanitizeApiKeyValue,
  sanitizeApiKeysObject,
  LEGACY_BULLET_PLACEHOLDER,
  LEGACY_PLATFORM_MANAGED_SENTINEL,
} from '../apiKeyPlaceholders.js';

describe('isPlaceholderApiKey', () => {
  test('detects the canonical bullet placeholder', () => {
    expect(isPlaceholderApiKey(LEGACY_BULLET_PLACEHOLDER)).toBe(true);
    expect(isPlaceholderApiKey('lx_••••••••••••••••(server-managed)')).toBe(true);
  });

  test('detects any string containing bullets', () => {
    expect(isPlaceholderApiKey('••••••••')).toBe(true);
    expect(isPlaceholderApiKey('lx_•••')).toBe(true);
  });

  test('detects the platform-managed sentinel exactly', () => {
    expect(isPlaceholderApiKey(LEGACY_PLATFORM_MANAGED_SENTINEL)).toBe(true);
    expect(isPlaceholderApiKey('platform-managed')).toBe(true);
  });

  test('detects (server-managed) and [server-managed] tags anywhere in the string', () => {
    expect(isPlaceholderApiKey('lx_abc(server-managed)')).toBe(true);
    expect(isPlaceholderApiKey('foo[server-managed]bar')).toBe(true);
    expect(isPlaceholderApiKey('(server-managed)')).toBe(true);
  });

  test('treats real-looking keys as not-placeholder', () => {
    expect(isPlaceholderApiKey('lx_1234567890abcdef')).toBe(false);
    expect(isPlaceholderApiKey('sk-proj-abcdefghijk')).toBe(false);
    expect(isPlaceholderApiKey('FfX3H7zwazdmDG3wIslbiawTYLVxXyzGKa97IaXcH9nUjVprZ0HqJQQJ99BEACfhMk5XJ3w3AAAAACOG6efc')).toBe(false);
  });

  test('returns false for empty / null / undefined / non-string', () => {
    expect(isPlaceholderApiKey('')).toBe(false);
    expect(isPlaceholderApiKey(null)).toBe(false);
    expect(isPlaceholderApiKey(undefined)).toBe(false);
    expect(isPlaceholderApiKey(123)).toBe(false);
    expect(isPlaceholderApiKey({})).toBe(false);
    expect(isPlaceholderApiKey([])).toBe(false);
  });

  test('does NOT treat the substring "managed" alone as a placeholder', () => {
    // Real keys should be allowed even if they happen to contain English
    // words. Only the bracketed/parenthesised tag triggers detection.
    expect(isPlaceholderApiKey('lx_managed_account_key_12345')).toBe(false);
    expect(isPlaceholderApiKey('server')).toBe(false);
  });
});

describe('sanitizeApiKeyValue', () => {
  test('passes real keys through unchanged', () => {
    expect(sanitizeApiKeyValue('lx_real_key_abc123')).toBe('lx_real_key_abc123');
  });

  test('strips placeholder values to empty string', () => {
    expect(sanitizeApiKeyValue(LEGACY_BULLET_PLACEHOLDER)).toBe('');
    expect(sanitizeApiKeyValue('platform-managed')).toBe('');
    expect(sanitizeApiKeyValue('foo(server-managed)')).toBe('');
  });

  test('returns "" for non-string inputs (avoids leaking nulls into UI state)', () => {
    expect(sanitizeApiKeyValue(null)).toBe('');
    expect(sanitizeApiKeyValue(undefined)).toBe('');
    expect(sanitizeApiKeyValue(0)).toBe('');
    expect(sanitizeApiKeyValue({ value: 'real' })).toBe('');
  });

  test('preserves empty string', () => {
    expect(sanitizeApiKeyValue('')).toBe('');
  });
});

describe('sanitizeApiKeysObject', () => {
  test('returns an empty object for null / undefined / non-object', () => {
    expect(sanitizeApiKeysObject(null)).toEqual({});
    expect(sanitizeApiKeysObject(undefined)).toEqual({});
    expect(sanitizeApiKeysObject('not-an-object')).toEqual({});
  });

  test('strips placeholders, preserves real keys', () => {
    const result = sanitizeApiKeysObject({
      loxiaBackend: LEGACY_BULLET_PLACEHOLDER,
      anthropic:    'sk-ant-real-key-1234567890',
      openai:       'platform-managed',
      azure:        'lx_real_azure_key',
      deepseek:     'foo(server-managed)bar',
    });
    expect(result).toEqual({
      loxiaBackend: '',
      anthropic:    'sk-ant-real-key-1234567890',
      openai:       '',
      azure:        'lx_real_azure_key',
      deepseek:     '',
    });
  });

  test('does not mutate the input', () => {
    const input = { loxiaBackend: LEGACY_BULLET_PLACEHOLDER };
    sanitizeApiKeysObject(input);
    expect(input.loxiaBackend).toBe(LEGACY_BULLET_PLACEHOLDER);
  });

  test('roundtrip: clean object passes through unchanged-shape', () => {
    const clean = { loxiaBackend: 'lx_abc', anthropic: 'sk-ant-xyz' };
    expect(sanitizeApiKeysObject(clean)).toEqual(clean);
  });
});
