import { describe, it, expect, vi, beforeEach } from 'vitest';
import { getMCPServersFromSettings } from '@/lib/settings-helpers.js';
import type { SettingsService } from '@/services/settings-service.js';

// Mock the logger
vi.mock('@automaker/utils', async () => {
  const actual = await vi.importActual('@automaker/utils');
  const mockLogger = {
    info: vi.fn(),
    error: vi.fn(),
    warn: vi.fn(),
    debug: vi.fn(),
  };
  return {
    ...actual,
    createLogger: () => mockLogger,
  };
});

describe('settings-helpers.ts', () => {
  describe('getMCPServersFromSettings', () => {
    beforeEach(() => {
      vi.clearAllMocks();
    });

    it('should return empty object when settingsService is null', async () => {
      const result = await getMCPServersFromSettings(null);
      expect(result).toEqual({});
    });

    it('should return empty object when settingsService is undefined', async () => {
      const result = await getMCPServersFromSettings(undefined);
      expect(result).toEqual({});
    });

    it('should return empty object when no MCP servers configured', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({ mcpServers: [] }),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(result).toEqual({});
    });

    it('should return empty object when mcpServers is undefined', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({}),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(result).toEqual({});
    });

    it('should convert enabled stdio server to SDK format', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({
          mcpServers: [
            {
              id: '1',
              name: 'test-server',
              type: 'stdio',
              command: 'node',
              args: ['server.js'],
              env: { NODE_ENV: 'test' },
              enabled: true,
            },
          ],
        }),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(result).toEqual({
        'test-server': {
          type: 'stdio',
          command: 'node',
          args: ['server.js'],
          env: { NODE_ENV: 'test' },
        },
      });
    });

    it('should convert enabled SSE server to SDK format', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({
          mcpServers: [
            {
              id: '1',
              name: 'sse-server',
              type: 'sse',
              url: 'http://localhost:3000/sse',
              headers: { Authorization: 'Bearer token' },
              enabled: true,
            },
          ],
        }),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(result).toEqual({
        'sse-server': {
          type: 'sse',
          url: 'http://localhost:3000/sse',
          headers: { Authorization: 'Bearer token' },
        },
      });
    });

    it('should convert enabled HTTP server to SDK format', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({
          mcpServers: [
            {
              id: '1',
              name: 'http-server',
              type: 'http',
              url: 'http://localhost:3000/api',
              headers: { 'X-API-Key': 'secret' },
              enabled: true,
            },
          ],
        }),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(result).toEqual({
        'http-server': {
          type: 'http',
          url: 'http://localhost:3000/api',
          headers: { 'X-API-Key': 'secret' },
        },
      });
    });

    it('should filter out disabled servers', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({
          mcpServers: [
            {
              id: '1',
              name: 'enabled-server',
              type: 'stdio',
              command: 'node',
              enabled: true,
            },
            {
              id: '2',
              name: 'disabled-server',
              type: 'stdio',
              command: 'python',
              enabled: false,
            },
          ],
        }),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(Object.keys(result)).toHaveLength(1);
      expect(result['enabled-server']).toBeDefined();
      expect(result['disabled-server']).toBeUndefined();
    });

    it('should treat servers without enabled field as enabled', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({
          mcpServers: [
            {
              id: '1',
              name: 'implicit-enabled',
              type: 'stdio',
              command: 'node',
              // enabled field not set
            },
          ],
        }),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(result['implicit-enabled']).toBeDefined();
    });

    it('should handle multiple enabled servers', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({
          mcpServers: [
            { id: '1', name: 'server1', type: 'stdio', command: 'node', enabled: true },
            { id: '2', name: 'server2', type: 'stdio', command: 'python', enabled: true },
          ],
        }),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(Object.keys(result)).toHaveLength(2);
      expect(result['server1']).toBeDefined();
      expect(result['server2']).toBeDefined();
    });

    it('should return empty object and log error on exception', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockRejectedValue(new Error('Settings error')),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService, '[Test]');
      expect(result).toEqual({});
      // Logger will be called with error, but we don't need to assert it
    });

    it('should throw error for SSE server without URL', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({
          mcpServers: [
            {
              id: '1',
              name: 'bad-sse',
              type: 'sse',
              enabled: true,
              // url missing
            },
          ],
        }),
      } as unknown as SettingsService;

      // The error is caught and logged, returns empty
      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(result).toEqual({});
    });

    it('should throw error for HTTP server without URL', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({
          mcpServers: [
            {
              id: '1',
              name: 'bad-http',
              type: 'http',
              enabled: true,
              // url missing
            },
          ],
        }),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(result).toEqual({});
    });

    it('should throw error for stdio server without command', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({
          mcpServers: [
            {
              id: '1',
              name: 'bad-stdio',
              type: 'stdio',
              enabled: true,
              // command missing
            },
          ],
        }),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(result).toEqual({});
    });

    it('should default to stdio type when type is not specified', async () => {
      const mockSettingsService = {
        getGlobalSettings: vi.fn().mockResolvedValue({
          mcpServers: [
            {
              id: '1',
              name: 'no-type',
              command: 'node',
              enabled: true,
              // type not specified, should default to stdio
            },
          ],
        }),
      } as unknown as SettingsService;

      const result = await getMCPServersFromSettings(mockSettingsService);
      expect(result['no-type']).toEqual({
        type: 'stdio',
        command: 'node',
        args: undefined,
        env: undefined,
      });
    });
  });
});
