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

// Mock auth
vi.mock('@/lib/auth', () => ({
  auth: vi.fn(),
}));

// Cloud router endpoint does not use the DB — no db mock needed.

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

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function makeRequest(body: Record<string, unknown>): NextRequest {
  return new NextRequest('http://localhost:3000/api/route', {
    method: 'POST',
    body: JSON.stringify(body),
    headers: { 'Content-Type': 'application/json' },
  });
}

function authedSession() {
  return { user: { id: 'user_1', email: 'test@test.com' } };
}

// ---------------------------------------------------------------------------
// Auth guard
// ---------------------------------------------------------------------------

describe('POST /api/route', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    // Default: authenticated user
    vi.mocked(auth).mockResolvedValue(authedSession() as never);
  });

  it('returns 401 if not authenticated', async () => {
    vi.mocked(auth).mockResolvedValue(null as never);

    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['llama3.1:8b'],
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'personal',
      context_length_tokens: 512,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(401);
    const data = await response.json();
    expect(data.error).toBe('Unauthorized');
  });

  it('returns 401 if session has no user id', async () => {
    vi.mocked(auth).mockResolvedValue({ user: {} } as never);

    const req = makeRequest({
      available_backends: ['llama3.1:8b'],
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'personal',
      context_length_tokens: 512,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(401);
  });

  // -------------------------------------------------------------------------
  // Validation
  // -------------------------------------------------------------------------

  it('returns 400 if available_backends is missing', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'personal',
      context_length_tokens: 512,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(400);
    const data = await response.json();
    expect(data.error).toBeDefined();
  });

  it('returns 400 if available_backends is empty array', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: [],
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'personal',
      context_length_tokens: 512,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(400);
  });

  it('returns 400 on invalid JSON body', async () => {
    const req = new NextRequest('http://localhost:3000/api/route', {
      method: 'POST',
      body: 'not-json',
      headers: { 'Content-Type': 'application/json' },
    });

    const response = await POST(req);
    expect(response.status).toBe(400);
  });

  // -------------------------------------------------------------------------
  // Cache-hit routing
  // -------------------------------------------------------------------------

  it('returns first backend with confidence 1.0 on cache_hit', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['llama3.1:8b', 'qwen3:14b'],
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'personal',
      context_length_tokens: 512,
      cache_hit: true,
    });

    const response = await POST(req);
    expect(response.status).toBe(200);

    const data = await response.json();
    expect(data.backend).toBe('llama3.1:8b');
    expect(data.confidence).toBe(1.0);
    expect(data.tier_used).toBe(0);
  });

  // -------------------------------------------------------------------------
  // Context-length routing
  // -------------------------------------------------------------------------

  it('prefers 70b model for long context (>8192 tokens)', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['llama3.1:8b', 'llama3.3:70b', 'qwen3:14b'],
      hardware_tier: 'rtx4090_24gb',
      privacy_tier: 'external',
      context_length_tokens: 10000,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(200);

    const data = await response.json();
    expect(data.backend).toBe('llama3.3:70b');
    expect(data.confidence).toBeGreaterThanOrEqual(0.8);
    expect(data.tier_used).toBe(0);
  });

  it('prefers 32b model for long context (>8192 tokens) when 70b absent', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['llama3.1:8b', 'qwen3:32b'],
      hardware_tier: 'rtx4090_24gb',
      privacy_tier: 'external',
      context_length_tokens: 9000,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(200);

    const data = await response.json();
    expect(data.backend).toBe('qwen3:32b');
    expect(data.tier_used).toBe(0);
  });

  // -------------------------------------------------------------------------
  // Privacy-tier routing
  // -------------------------------------------------------------------------

  it('prefers 8b model for personal privacy tier', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['qwen3:14b', 'llama3.1:8b', 'llama3.3:70b'],
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'personal',
      context_length_tokens: 200,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(200);

    const data = await response.json();
    expect(data.backend).toBe('llama3.1:8b');
    expect(data.tier_used).toBe(0);
  });

  it('prefers 7b model for personal privacy tier when 8b absent', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['qwen3:14b', 'mistral:7b'],
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'personal',
      context_length_tokens: 200,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(200);

    const data = await response.json();
    expect(data.backend).toBe('mistral:7b');
  });

  it('prefers 3b model for personal privacy tier when 7b and 8b absent', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['qwen3:14b', 'qwen3:3b'],
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'personal',
      context_length_tokens: 200,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(200);

    const data = await response.json();
    expect(data.backend).toBe('qwen3:3b');
  });

  // -------------------------------------------------------------------------
  // Default routing
  // -------------------------------------------------------------------------

  it('returns first backend with tier_used=0 for default case', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['llama3.1:8b', 'qwen3:14b'],
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'external',
      context_length_tokens: 512,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(200);

    const data = await response.json();
    expect(data.backend).toBe('llama3.1:8b');
    expect(data.tier_used).toBe(0);
    expect(data.confidence).toBeDefined();
    expect(data.reasoning).toBeDefined();
  });

  // -------------------------------------------------------------------------
  // Response shape
  // -------------------------------------------------------------------------

  it('response always contains backend, confidence, tier_used, reasoning', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['llama3.1:8b'],
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'external',
      context_length_tokens: 256,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(200);

    const data = await response.json();
    expect(typeof data.backend).toBe('string');
    expect(typeof data.confidence).toBe('number');
    expect(typeof data.tier_used).toBe('number');
    expect(typeof data.reasoning).toBe('string');
  });

  it('tier_used is always 0 (rule-based phase)', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['llama3.1:8b', 'qwen3:14b'],
      hardware_tier: 'rtx4090_24gb',
      privacy_tier: 'external',
      context_length_tokens: 512,
      cache_hit: false,
    });

    const response = await POST(req);
    const data = await response.json();

    // Tier 0 = rule-based. Tier 1/2 require #285 (ML fine-tuning).
    expect(data.tier_used).toBe(0);
  });

  it('single backend is always returned when only one available', async () => {
    const req = makeRequest({
      request_hash: 'abc123',
      available_backends: ['only-model:14b'],
      hardware_tier: 'rtx4060_8gb',
      privacy_tier: 'external',
      context_length_tokens: 512,
      cache_hit: false,
    });

    const response = await POST(req);
    expect(response.status).toBe(200);

    const data = await response.json();
    expect(data.backend).toBe('only-model:14b');
  });
});
