import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GET } from '@/app/api/atlas/route';
import { NextRequest } from 'next/server';

// Mock Prisma db
vi.mock('@/lib/db', () => ({
  db: {
    atlasEntry: {
      findMany: vi.fn(),
    },
  },
}));

import { db } from '@/lib/db';

const mockEntries = [
  {
    id: 'cuid1',
    gpuModel: 'RTX 4060',
    vramMb: 8192,
    modelId: 'qwen3:8b',
    quantization: 'Q4_K_M',
    tpsP50: 45.7,
    tpsP10: 30.2,
    successRate: 0.98,
    measurementCount: 120,
    firstTokenLatencyMs: 312.5,
    updatedAt: new Date('2026-02-24T00:00:00Z'),
  },
  {
    id: 'cuid2',
    gpuModel: 'RTX 4090',
    vramMb: 24576,
    modelId: 'llama3.3:70b',
    quantization: 'Q4_K_M',
    tpsP50: 22.1,
    tpsP10: 15.0,
    successRate: 0.95,
    measurementCount: 75,
    firstTokenLatencyMs: 800.0,
    updatedAt: new Date('2026-02-24T00:00:00Z'),
  },
];

function makeRequest(url: string): NextRequest {
  return new NextRequest(url);
}

describe('GET /api/atlas', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should return entries without filters', async () => {
    vi.mocked(db.atlasEntry.findMany).mockResolvedValue(mockEntries as any);

    const request = makeRequest('http://localhost/api/atlas');
    const response = await GET(request);
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data).toHaveLength(2);
    expect(data[0].gpuModel).toBe('RTX 4060');
    expect(data[1].gpuModel).toBe('RTX 4090');

    expect(db.atlasEntry.findMany).toHaveBeenCalledWith({
      where: {},
      orderBy: { measurementCount: 'desc' },
      take: 50,
    });
  });

  it('should filter by gpu name (case insensitive)', async () => {
    const filtered = mockEntries.filter((e) => e.gpuModel.includes('RTX 4060'));
    vi.mocked(db.atlasEntry.findMany).mockResolvedValue(filtered as any);

    const request = makeRequest('http://localhost/api/atlas?gpu=RTX+4060');
    const response = await GET(request);
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(db.atlasEntry.findMany).toHaveBeenCalledWith({
      where: { gpuModel: { contains: 'RTX 4060', mode: 'insensitive' } },
      orderBy: { measurementCount: 'desc' },
      take: 50,
    });
    expect(data).toHaveLength(1);
    expect(data[0].gpuModel).toBe('RTX 4060');
  });

  it('should filter by vram_mb', async () => {
    const filtered = mockEntries.filter((e) => e.vramMb === 8192);
    vi.mocked(db.atlasEntry.findMany).mockResolvedValue(filtered as any);

    const request = makeRequest('http://localhost/api/atlas?vram_mb=8192');
    const response = await GET(request);
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(db.atlasEntry.findMany).toHaveBeenCalledWith({
      where: { vramMb: 8192 },
      orderBy: { measurementCount: 'desc' },
      take: 50,
    });
    expect(data).toHaveLength(1);
  });

  it('should filter by both gpu and vram_mb', async () => {
    vi.mocked(db.atlasEntry.findMany).mockResolvedValue([mockEntries[0]] as any);

    const request = makeRequest('http://localhost/api/atlas?gpu=RTX&vram_mb=8192');
    const response = await GET(request);

    expect(response.status).toBe(200);
    expect(db.atlasEntry.findMany).toHaveBeenCalledWith({
      where: {
        gpuModel: { contains: 'RTX', mode: 'insensitive' },
        vramMb: 8192,
      },
      orderBy: { measurementCount: 'desc' },
      take: 50,
    });
  });

  it('should return 200 with empty array when no entries match', async () => {
    vi.mocked(db.atlasEntry.findMany).mockResolvedValue([]);

    const request = makeRequest('http://localhost/api/atlas?gpu=NonexistentGPU');
    const response = await GET(request);
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data).toEqual([]);
  });

  it('should return 500 on database error', async () => {
    vi.mocked(db.atlasEntry.findMany).mockRejectedValue(new Error('Database connection failed'));

    const request = makeRequest('http://localhost/api/atlas');
    const response = await GET(request);
    const data = await response.json();

    expect(response.status).toBe(500);
    expect(data.error).toBe('Failed to load atlas data');
  });

  it('should limit results to 50 entries', async () => {
    vi.mocked(db.atlasEntry.findMany).mockResolvedValue(mockEntries as any);

    const request = makeRequest('http://localhost/api/atlas');
    await GET(request);

    expect(db.atlasEntry.findMany).toHaveBeenCalledWith(
      expect.objectContaining({ take: 50 }),
    );
  });
});
