import { describe, it, expect } from 'vitest';
import { authenticateRequest } from './auth.js';
import type { IncomingMessage } from 'http';

function mockRequest(headers: Record<string, string> = {}): IncomingMessage {
  return { headers } as unknown as IncomingMessage;
}

function mockRequestWithUrl(url: string, headers: Record<string, string> = {}): IncomingMessage {
  return { url, headers } as unknown as IncomingMessage;
}

describe('authenticateRequest', () => {
  describe('no auth configured', () => {
    it('allows all connections', () => {
      const result = authenticateRequest(mockRequest(), {});
      expect(result.authenticated).toBe(true);
      expect(result.identity).toBe('anonymous');
    });
  });

  describe('token auth', () => {
    const config = { token: 'secret-token-123' };

    it('accepts valid Bearer token', () => {
      const result = authenticateRequest(
        mockRequest({ authorization: 'Bearer secret-token-123' }),
        config,
      );
      expect(result.authenticated).toBe(true);
      expect(result.identity).toBe('token-user');
    });

    it('rejects missing Authorization header', () => {
      const result = authenticateRequest(mockRequest(), config);
      expect(result.authenticated).toBe(false);
      expect(result.error).toContain('Authorization required');
    });

    it('rejects invalid token', () => {
      const result = authenticateRequest(
        mockRequest({ authorization: 'Bearer wrong-token' }),
        config,
      );
      expect(result.authenticated).toBe(false);
      expect(result.error).toContain('Invalid token');
    });

    it('rejects non-Bearer format', () => {
      const result = authenticateRequest(
        mockRequest({ authorization: 'Basic dXNlcjpwYXNz' }),
        config,
      );
      expect(result.authenticated).toBe(false);
      expect(result.error).toContain('Invalid Authorization format');
    });

    it('uses Tailscale identity when both token and tailscale are configured', () => {
      const result = authenticateRequest(
        mockRequest({
          authorization: 'Bearer secret-token-123',
          'tailscale-user-login': 'will@example.com',
        }),
        { token: 'secret-token-123', tailscaleIdentity: true },
      );
      expect(result.authenticated).toBe(true);
      expect(result.identity).toBe('will@example.com');
    });
  });

  describe('tailscale identity', () => {
    const config = { tailscaleIdentity: true };

    it('extracts identity from Tailscale-User-Login header', () => {
      const result = authenticateRequest(
        mockRequest({ 'tailscale-user-login': 'will@example.com' }),
        config,
      );
      expect(result.authenticated).toBe(true);
      expect(result.identity).toBe('will@example.com');
    });

    it('allows connections without Tailscale header (local access)', () => {
      const result = authenticateRequest(mockRequest(), config);
      expect(result.authenticated).toBe(true);
      expect(result.identity).toBe('anonymous');
    });
  });

  describe('query parameter token', () => {
    const config = { token: 'secret-token-123' };

    it('accepts valid token in query parameter', () => {
      const result = authenticateRequest(
        mockRequestWithUrl('/?token=secret-token-123'),
        config,
      );
      expect(result.authenticated).toBe(true);
      expect(result.identity).toBe('token-user');
    });

    it('rejects invalid token in query parameter', () => {
      const result = authenticateRequest(
        mockRequestWithUrl('/?token=wrong'),
        config,
      );
      expect(result.authenticated).toBe(false);
      expect(result.error).toContain('Invalid token');
    });

    it('prefers header over query parameter', () => {
      const result = authenticateRequest(
        mockRequestWithUrl('/?token=wrong', { authorization: 'Bearer secret-token-123' }),
        config,
      );
      expect(result.authenticated).toBe(true);
      expect(result.identity).toBe('token-user');
    });

    it('rejects when neither header nor query parameter provided', () => {
      const result = authenticateRequest(
        mockRequestWithUrl('/'),
        config,
      );
      expect(result.authenticated).toBe(false);
      expect(result.error).toContain('Authorization required');
    });
  });
});
