import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { classifyIntent, selectModelForIntent, type TokenIntent } from '@/lib/token/classifier';

// Mock logger
vi.mock('@/lib/logger', () => ({
  logger: { info: vi.fn(), error: vi.fn(), warn: vi.fn() },
}));

describe('classifyIntent', () => {
  const originalEnv = process.env.OPENROUTER_API_KEY;

  beforeEach(() => {
    vi.clearAllMocks();
    process.env.OPENROUTER_API_KEY = 'test-openrouter-key';
  });

  afterEach(() => {
    vi.restoreAllMocks();
    if (originalEnv !== undefined) {
      process.env.OPENROUTER_API_KEY = originalEnv;
    } else {
      delete process.env.OPENROUTER_API_KEY;
    }
  });

  function mockFetchResponse(content: string, ok = true, status = 200) {
    const mockResponse = {
      ok,
      status,
      json: async () => ({
        choices: [{ message: { content } }],
      }),
      text: async () => content,
      headers: new Headers(),
    };
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue(mockResponse));
  }

  it('should classify chat messages correctly', async () => {
    // Arrange
    mockFetchResponse('{"intent": "chat", "confidence": 0.95, "reasoning": "Greeting message"}');

    // Act
    const result = await classifyIntent('hi how are you', 'google/gemini-2.5-flash-lite');

    // Assert
    expect(result.intent).toBe('chat');
    expect(result.confidence).toBe(0.95);
    expect(result.reasoning).toBe('Greeting message');
  });

  it('should classify research messages correctly', async () => {
    // Arrange
    mockFetchResponse(
      '{"intent": "research", "confidence": 0.9, "reasoning": "User asking about model recommendations"}',
    );

    // Act
    const result = await classifyIntent(
      "what's the best model for 8GB VRAM?",
      'google/gemini-2.5-flash-lite',
    );

    // Assert
    expect(result.intent).toBe('research');
    expect(result.confidence).toBe(0.9);
  });

  it('should classify fleet action messages correctly', async () => {
    // Arrange
    mockFetchResponse(
      '{"intent": "fleet_action", "confidence": 0.92, "reasoning": "Direct install command"}',
    );

    // Act
    const result = await classifyIntent(
      'install qwen3:8b on my desktop',
      'google/gemini-2.5-flash-lite',
    );

    // Assert
    expect(result.intent).toBe('fleet_action');
    expect(result.confidence).toBe(0.92);
  });

  it('should classify analysis messages correctly', async () => {
    // Arrange
    mockFetchResponse(
      '{"intent": "analysis", "confidence": 0.88, "reasoning": "Multi-device deployment planning"}',
    );

    // Act
    const result = await classifyIntent(
      'plan a deployment across my 3 machines',
      'google/gemini-2.5-flash-lite',
    );

    // Assert
    expect(result.intent).toBe('analysis');
    expect(result.confidence).toBe(0.88);
  });

  it('should fall back to chat on low confidence', async () => {
    // Arrange
    mockFetchResponse(
      '{"intent": "research", "confidence": 0.3, "reasoning": "Ambiguous message"}',
    );

    // Act
    const result = await classifyIntent('something vague', 'google/gemini-2.5-flash-lite');

    // Assert
    expect(result.intent).toBe('chat');
    expect(result.confidence).toBe(0.3);
    expect(result.reasoning).toContain('Low confidence');
  });

  it('should fall back to chat on API error', async () => {
    // Arrange
    mockFetchResponse('Internal Server Error', false, 500);

    // Act
    const result = await classifyIntent('hello', 'google/gemini-2.5-flash-lite');

    // Assert
    expect(result.intent).toBe('chat');
    expect(result.confidence).toBe(1.0);
    expect(result.reasoning).toContain('API error');
  });

  it('should fall back to chat on missing API key', async () => {
    // Arrange
    delete process.env.OPENROUTER_API_KEY;

    // Act
    const result = await classifyIntent('hello', 'google/gemini-2.5-flash-lite');

    // Assert
    expect(result.intent).toBe('chat');
    expect(result.confidence).toBe(1.0);
    expect(result.reasoning).toContain('No API key');
  });

  it('should fall back to chat on malformed JSON response', async () => {
    // Arrange
    mockFetchResponse('This is not JSON at all, just plain text with no braces.');

    // Act
    const result = await classifyIntent('test message', 'google/gemini-2.5-flash-lite');

    // Assert
    expect(result.intent).toBe('chat');
    expect(result.confidence).toBe(1.0);
    expect(result.reasoning).toContain('Parse failure');
  });

  it('should validate unknown intents and fall back to chat', async () => {
    // Arrange
    mockFetchResponse(
      '{"intent": "unknown_type", "confidence": 0.9, "reasoning": "Unknown category"}',
    );

    // Act
    const result = await classifyIntent('do something weird', 'google/gemini-2.5-flash-lite');

    // Assert
    expect(result.intent).toBe('chat');
    expect(result.confidence).toBe(1.0);
    expect(result.reasoning).toContain('Unknown intent');
  });

  it('should handle fetch throwing an exception', async () => {
    // Arrange
    vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('Network error')));

    // Act
    const result = await classifyIntent('hello', 'google/gemini-2.5-flash-lite');

    // Assert
    expect(result.intent).toBe('chat');
    expect(result.confidence).toBe(1.0);
    expect(result.reasoning).toContain('Classification error');
  });
});

describe('selectModelForIntent', () => {
  const config = {
    chatModel: 'google/gemini-2.5-flash',
    researchModel: 'anthropic/claude-sonnet-4.6',
    reasoningModel: 'deepseek/deepseek-r1',
  };

  it('should pick chatModel for chat intent', () => {
    expect(selectModelForIntent('chat', config)).toBe('google/gemini-2.5-flash');
  });

  it('should pick researchModel for research intent', () => {
    expect(selectModelForIntent('research', config)).toBe('anthropic/claude-sonnet-4.6');
  });

  it('should pick chatModel for fleet_action intent', () => {
    expect(selectModelForIntent('fleet_action', config)).toBe('google/gemini-2.5-flash');
  });

  it('should pick reasoningModel for analysis intent', () => {
    expect(selectModelForIntent('analysis', config)).toBe('deepseek/deepseek-r1');
  });

  it('should default to chatModel for unknown intent', () => {
    expect(selectModelForIntent('something_else' as TokenIntent, config)).toBe(
      'google/gemini-2.5-flash',
    );
  });
});
