import { describe, it, expect, vi } from 'vitest';
import { createProviderFromConfig } from '../src/llm-config.js';
import type { SecretStore } from '@mcpctl/shared';

function mockSecretStore(secrets: Record<string, string> = {}): SecretStore {
  return {
    get: vi.fn(async (key: string) => secrets[key] ?? null),
    set: vi.fn(async () => {}),
    delete: vi.fn(async () => true),
    backend: () => 'mock',
  };
}

describe('createProviderFromConfig', () => {
  it('returns empty registry for undefined config', async () => {
    const store = mockSecretStore();
    const registry = await createProviderFromConfig(undefined, store);
    expect(registry.getActive()).toBeNull();
    expect(registry.list()).toEqual([]);
  });

  it('returns empty registry for provider=none', async () => {
    const store = mockSecretStore();
    const registry = await createProviderFromConfig({ provider: 'none' }, store);
    expect(registry.getActive()).toBeNull();
  });

  it('creates gemini-cli provider using ACP', async () => {
    const store = mockSecretStore();
    const registry = await createProviderFromConfig(
      { provider: 'gemini-cli', model: 'gemini-2.5-flash', binaryPath: '/usr/bin/gemini' },
      store,
    );
    expect(registry.getActive()).not.toBeNull();
    expect(registry.getActive()!.name).toBe('gemini-cli');
    // ACP provider has dispose method
    expect(typeof registry.getActive()!.dispose).toBe('function');
  });

  it('creates ollama provider', async () => {
    const store = mockSecretStore();
    const registry = await createProviderFromConfig(
      { provider: 'ollama', model: 'llama3.2', url: 'http://localhost:11434' },
      store,
    );
    expect(registry.getActive()!.name).toBe('ollama');
  });

  it('creates anthropic provider with API key from secret store', async () => {
    const store = mockSecretStore({ 'anthropic-api-key': 'sk-ant-test' });
    const registry = await createProviderFromConfig(
      { provider: 'anthropic', model: 'claude-haiku-3-5-20241022' },
      store,
    );
    expect(registry.getActive()!.name).toBe('anthropic');
    expect(store.get).toHaveBeenCalledWith('anthropic-api-key');
  });

  it('returns empty registry when anthropic API key is missing', async () => {
    const store = mockSecretStore();
    const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
    const registry = await createProviderFromConfig(
      { provider: 'anthropic', model: 'claude-haiku-3-5-20241022' },
      store,
    );
    expect(registry.getActive()).toBeNull();
    expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('Anthropic API key not found'));
    stderrSpy.mockRestore();
  });

  it('creates openai provider with API key from secret store', async () => {
    const store = mockSecretStore({ 'openai-api-key': 'sk-test' });
    const registry = await createProviderFromConfig(
      { provider: 'openai', model: 'gpt-4o', url: 'https://api.openai.com' },
      store,
    );
    expect(registry.getActive()!.name).toBe('openai');
    expect(store.get).toHaveBeenCalledWith('openai-api-key');
  });

  it('returns empty registry when openai API key is missing', async () => {
    const store = mockSecretStore();
    const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
    const registry = await createProviderFromConfig(
      { provider: 'openai' },
      store,
    );
    expect(registry.getActive()).toBeNull();
    stderrSpy.mockRestore();
  });

  it('creates deepseek provider with API key from secret store', async () => {
    const store = mockSecretStore({ 'deepseek-api-key': 'sk-ds-test' });
    const registry = await createProviderFromConfig(
      { provider: 'deepseek', model: 'deepseek-chat' },
      store,
    );
    expect(registry.getActive()!.name).toBe('deepseek');
    expect(store.get).toHaveBeenCalledWith('deepseek-api-key');
  });

  it('returns empty registry when deepseek API key is missing', async () => {
    const store = mockSecretStore();
    const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
    const registry = await createProviderFromConfig(
      { provider: 'deepseek' },
      store,
    );
    expect(registry.getActive()).toBeNull();
    stderrSpy.mockRestore();
  });

  it('creates vllm provider using OpenAI provider', async () => {
    const store = mockSecretStore();
    const registry = await createProviderFromConfig(
      { provider: 'vllm', model: 'my-model', url: 'http://gpu-server:8000' },
      store,
    );
    // vLLM reuses OpenAI provider under the hood, wrapped with NamedProvider
    expect(registry.getActive()).not.toBeNull();
    expect(registry.getActive()!.name).toBe('vllm');
  });

  it('returns empty registry when vllm URL is missing', async () => {
    const store = mockSecretStore();
    const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
    const registry = await createProviderFromConfig(
      { provider: 'vllm' },
      store,
    );
    expect(registry.getActive()).toBeNull();
    expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('vLLM URL not configured'));
    stderrSpy.mockRestore();
  });
});
