/**
 * Integration tests for authentication flows
 * Tests API key, JWT, and mixed mode authentication
 */

import { describe, it, expect, beforeAll } from '@jest/globals';
import {
  initTestServer,
  authenticatedRequest,
  jwtAuthenticatedRequest,
  unauthenticatedRequest,
  waitForServer,
} from './helpers/test-server';
import * as jwt from 'jsonwebtoken';

describe('Authentication Integration', () => {
  beforeAll(async () => {
    initTestServer();
    const isReady = await waitForServer(30000);
    if (!isReady) {
      throw new Error('Test server is not ready');
    }
  });

  describe('API Key Authentication', () => {
    it('should accept valid API key', async () => {
      const response = await authenticatedRequest('/api/runs', {
        method: 'GET',
      });

      // Should not return 401 Unauthorized
      expect(response.status).not.toBe(401);
    });

    it('should reject invalid API key', async () => {
      const response = await unauthenticatedRequest('/api/runs', {
        method: 'GET',
        headers: {
          'x-api-key': 'invalid-key',
        },
      });

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

    it('should reject missing API key when auth is enabled', async () => {
      const response = await unauthenticatedRequest('/api/runs', {
        method: 'GET',
      });

      // If auth is enabled, should return 401
      // If auth is disabled, might return 200 or 400
      if (response.status === 401) {
        const data = await response.json();
        expect(data).toHaveProperty('error');
      }
    });
  });

  describe('JWT Authentication', () => {
    it('should accept valid JWT token', async () => {
      const jwtSecret = process.env.JWT_SECRET || 'test_jwt_secret_at_least_32_chars_long_123';
      const token = jwt.sign(
        { sub: 'test-user-123', exp: Math.floor(Date.now() / 1000) + 3600 },
        jwtSecret,
        { algorithm: 'HS256' }
      );

      const response = await jwtAuthenticatedRequest('/api/runs', token, {
        method: 'GET',
      });

      // Should not return 401 if JWT auth is enabled
      if (process.env.AUTH_MODE === 'jwt' || process.env.AUTH_MODE === 'mixed') {
        expect(response.status).not.toBe(401);
      }
    });

    it('should reject invalid JWT token', async () => {
      const response = await jwtAuthenticatedRequest('/api/runs', 'invalid-token', {
        method: 'GET',
      });

      // Should return 401 for invalid token
      if (process.env.AUTH_MODE === 'jwt' || process.env.AUTH_MODE === 'mixed') {
        expect(response.status).toBe(401);
      }
    });

    it('should reject expired JWT token', async () => {
      const jwtSecret = process.env.JWT_SECRET || 'test_jwt_secret_at_least_32_chars_long_123';
      const expiredToken = jwt.sign(
        { sub: 'test-user-123', exp: Math.floor(Date.now() / 1000) - 3600 }, // Expired 1 hour ago
        jwtSecret,
        { algorithm: 'HS256' }
      );

      const response = await jwtAuthenticatedRequest('/api/runs', expiredToken, {
        method: 'GET',
      });

      if (process.env.AUTH_MODE === 'jwt' || process.env.AUTH_MODE === 'mixed') {
        expect(response.status).toBe(401);
      }
    });
  });

  describe('Mixed Mode Authentication', () => {
    it('should accept either API key or JWT', async () => {
      // Test with API key
      const apiKeyResponse = await authenticatedRequest('/api/runs', {
        method: 'GET',
      });

      // Test with JWT
      const jwtSecret = process.env.JWT_SECRET || 'test_jwt_secret_at_least_32_chars_long_123';
      const token = jwt.sign(
        { sub: 'test-user-123', exp: Math.floor(Date.now() / 1000) + 3600 },
        jwtSecret,
        { algorithm: 'HS256' }
      );

      const jwtResponse = await jwtAuthenticatedRequest('/api/runs', token, {
        method: 'GET',
      });

      if (process.env.AUTH_MODE === 'mixed') {
        // Both should work
        expect(apiKeyResponse.status).not.toBe(401);
        expect(jwtResponse.status).not.toBe(401);
      }
    });
  });

  describe('Public Endpoints', () => {
    it('should allow access to health endpoint without auth', async () => {
      const response = await unauthenticatedRequest('/health');
      expect(response.status).toBe(200);
    });

    it('should allow access to ready endpoint without auth', async () => {
      const response = await unauthenticatedRequest('/ready');
      expect(response.status).toBe(200);
    });

    it('should allow access to metrics endpoint without auth', async () => {
      const response = await unauthenticatedRequest('/metrics');
      // Metrics might require auth in production, but should be accessible
      expect([200, 401]).toContain(response.status);
    });
  });
});

