/**
 * OIDC Provider Tests
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { OIDCProvider } from './oidc';
import fetch from 'node-fetch';

vi.mock('node-fetch');

describe('OIDCProvider', () => {
  let provider: OIDCProvider;
  const config = {
    issuer: 'https://auth.example.com',
    clientId: 'test-client-id',
    clientSecret: 'test-client-secret',
    redirectUri: 'https://app.example.com/callback',
  };

  beforeEach(() => {
    provider = new OIDCProvider(config);
    vi.clearAllMocks();
  });

  describe('getAuthorizationUrl', () => {
    it('should generate correct authorization URL', () => {
      const state = 'test-state-123';
      const url = provider.getAuthorizationUrl(state);

      expect(url).toContain(config.issuer);
      expect(url).toContain('/authorize');
      expect(url).toContain(`client_id=${config.clientId}`);
      expect(url).toContain(`redirect_uri=${encodeURIComponent(config.redirectUri)}`);
      expect(url).toContain(`state=${state}`);
      expect(url).toContain('response_type=code');
      expect(url).toContain('scope=openid profile email');
    });
  });

  describe('exchangeCodeForToken', () => {
    it('should exchange authorization code for access token', async () => {
      const code = 'test-auth-code';
      const mockResponse = {
        access_token: 'test-access-token',
        token_type: 'Bearer',
        expires_in: 3600,
        refresh_token: 'test-refresh-token',
      };

      (fetch as any).mockResolvedValueOnce({
        ok: true,
        json: async () => mockResponse,
      });

      const token = await provider.exchangeCodeForToken(code);

      expect(token).toBe(mockResponse.access_token);
      expect(fetch).toHaveBeenCalledWith(
        `${config.issuer}/token`,
        expect.objectContaining({
          method: 'POST',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
          },
          body: expect.stringContaining(`code=${code}`),
        })
      );
    });

    it('should throw error on failed token exchange', async () => {
      const code = 'invalid-code';

      (fetch as any).mockResolvedValueOnce({
        ok: false,
        status: 400,
        text: async () => 'Invalid grant',
      });

      await expect(provider.exchangeCodeForToken(code)).rejects.toThrow(
        'Token exchange failed'
      );
    });
  });
});
