import { describe, expect, it, vi, beforeEach } from 'vitest';
import Fastify from 'fastify';
import { llmRoutes } from './routes.js';

// Mock feature flags
vi.mock('../../lib/feature-flags.js', () => ({
  isFeatureEnabled: vi.fn((flag: string) => flag === 'llm_multi_model_enabled'),
}));

// Mock LLM router
const mockChat = vi.fn();
const mockGetProviders = vi.fn(() => ['groq', 'openrouter']);
const mockGetHealth = vi.fn(() => [
  { provider: 'groq', model: 'llama-3.3-70b-versatile', healthy: true, totalRequests: 5, successes: 5, rateLimits: 0, errors: 0, avgLatencyMs: 200, p95LatencyMs: 300 },
]);

vi.mock('../../lib/llm-router.js', () => ({
  getLlmRouter: vi.fn(() => ({
    chat: mockChat,
    getProviders: mockGetProviders,
    getHealth: mockGetHealth,
  })),
  isLlmRouterReady: vi.fn(() => true),
}));

import { isFeatureEnabled } from '../../lib/feature-flags.js';
import { isLlmRouterReady } from '../../lib/llm-router.js';

async function buildApp() {
  const app = Fastify({ logger: false });
  await app.register(llmRoutes);
  return app;
}

beforeEach(() => {
  vi.clearAllMocks();
  vi.mocked(isFeatureEnabled).mockReturnValue(true);
  vi.mocked(isLlmRouterReady).mockReturnValue(true);
});

describe('POST /api/llm/chat', () => {
  it('routes a valid chat request', async () => {
    mockChat.mockResolvedValue({
      response: { id: 'r1', object: 'chat.completion', created: 1, model: 'llama-3.3-70b', choices: [{ index: 0, message: { role: 'assistant', content: 'Hello' }, finish_reason: 'stop' }] },
      provider: 'groq',
      model: 'llama-3.3-70b-versatile',
      totalLatencyMs: 150,
      attempts: 1,
    });

    const app = await buildApp();
    const res = await app.inject({
      method: 'POST',
      url: '/api/llm/chat',
      payload: { messages: [{ role: 'user', content: 'Hi' }] },
    });

    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    expect(body.provider).toBe('groq');
    expect(body.model).toBe('llama-3.3-70b-versatile');
    expect(mockChat).toHaveBeenCalledOnce();
  });

  it('returns 400 for invalid request body', async () => {
    const app = await buildApp();
    const res = await app.inject({
      method: 'POST',
      url: '/api/llm/chat',
      payload: { messages: [] },
    });

    expect(res.statusCode).toBe(400);
    expect(JSON.parse(res.body).error).toBe('Invalid request');
  });

  it('returns 403 when flag is disabled', async () => {
    vi.mocked(isFeatureEnabled).mockReturnValue(false);
    const app = await buildApp();
    const res = await app.inject({
      method: 'POST',
      url: '/api/llm/chat',
      payload: { messages: [{ role: 'user', content: 'Hi' }] },
    });

    expect(res.statusCode).toBe(403);
  });

  it('returns 503 when router not ready', async () => {
    vi.mocked(isLlmRouterReady).mockReturnValue(false);
    const app = await buildApp();
    const res = await app.inject({
      method: 'POST',
      url: '/api/llm/chat',
      payload: { messages: [{ role: 'user', content: 'Hi' }] },
    });

    expect(res.statusCode).toBe(503);
  });

  it('returns 502 on router error', async () => {
    mockChat.mockRejectedValue(new Error('All providers exhausted'));
    const app = await buildApp();
    const res = await app.inject({
      method: 'POST',
      url: '/api/llm/chat',
      payload: { messages: [{ role: 'user', content: 'Hi' }] },
    });

    expect(res.statusCode).toBe(502);
    expect(JSON.parse(res.body).error).toContain('All providers exhausted');
  });
});

describe('GET /api/llm/providers', () => {
  it('returns provider list', async () => {
    const app = await buildApp();
    const res = await app.inject({ method: 'GET', url: '/api/llm/providers' });

    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    expect(body.providers).toEqual(['groq', 'openrouter']);
    expect(body.ready).toBe(true);
  });

  it('returns empty when router not ready', async () => {
    vi.mocked(isLlmRouterReady).mockReturnValue(false);
    const app = await buildApp();
    const res = await app.inject({ method: 'GET', url: '/api/llm/providers' });

    expect(res.statusCode).toBe(200);
    expect(JSON.parse(res.body)).toEqual({ providers: [], ready: false });
  });
});

describe('GET /api/llm/health', () => {
  it('returns health snapshots', async () => {
    const app = await buildApp();
    const res = await app.inject({ method: 'GET', url: '/api/llm/health' });

    expect(res.statusCode).toBe(200);
    const body = JSON.parse(res.body);
    expect(body.snapshots).toHaveLength(1);
    expect(body.snapshots[0].provider).toBe('groq');
    expect(body.ready).toBe(true);
  });
});
