import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { buildOAuthProviders } from './auth.js';

describe('buildOAuthProviders', () => {
  const originalEnv = process.env;

  beforeEach(() => {
    process.env = { ...originalEnv };
    delete process.env['AUTHENTIK_CLIENT_ID'];
    delete process.env['AUTHENTIK_CLIENT_SECRET'];
    delete process.env['AUTHENTIK_ISSUER'];
    delete process.env['WORKOS_CLIENT_ID'];
    delete process.env['WORKOS_CLIENT_SECRET'];
    delete process.env['WORKOS_ISSUER'];
    delete process.env['KEYCLOAK_CLIENT_ID'];
    delete process.env['KEYCLOAK_CLIENT_SECRET'];
    delete process.env['KEYCLOAK_ISSUER'];
    delete process.env['KEYCLOAK_URL'];
    delete process.env['KEYCLOAK_REALM'];
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  it('returns empty array when no SSO env vars are set', () => {
    const providers = buildOAuthProviders();
    expect(providers).toHaveLength(0);
  });

  describe('WorkOS', () => {
    it('includes workos provider when all required env vars are set', () => {
      process.env['WORKOS_CLIENT_ID'] = 'client_test123';
      process.env['WORKOS_CLIENT_SECRET'] = 'sk_live_test';
      process.env['WORKOS_ISSUER'] = 'https://example.authkit.app/';

      const providers = buildOAuthProviders();
      const workos = providers.find((p) => p.providerId === 'workos');

      expect(workos).toBeDefined();
      expect(workos?.clientId).toBe('client_test123');
      expect(workos?.issuer).toBe('https://example.authkit.app');
      expect(workos?.discoveryUrl).toBe(
        'https://example.authkit.app/.well-known/openid-configuration',
      );
      expect(workos?.scopes).toEqual(['openid', 'email', 'profile']);
    });

    it('throws when WorkOS is partially configured', () => {
      process.env['WORKOS_CLIENT_ID'] = 'client_test123';

      expect(() => buildOAuthProviders()).toThrow(
        '@mosaicstack/auth: WorkOS SSO requires WORKOS_ISSUER, WORKOS_CLIENT_ID, WORKOS_CLIENT_SECRET.',
      );
    });

    it('excludes workos provider when WorkOS is not configured', () => {
      const providers = buildOAuthProviders();
      const workos = providers.find((p) => p.providerId === 'workos');
      expect(workos).toBeUndefined();
    });
  });

  describe('Keycloak', () => {
    it('includes keycloak provider when KEYCLOAK_ISSUER is set', () => {
      process.env['KEYCLOAK_CLIENT_ID'] = 'mosaic';
      process.env['KEYCLOAK_CLIENT_SECRET'] = 'secret123';
      process.env['KEYCLOAK_ISSUER'] = 'https://auth.example.com/realms/myrealm/';

      const providers = buildOAuthProviders();
      const keycloakProvider = providers.find((p) => p.providerId === 'keycloak');

      expect(keycloakProvider).toBeDefined();
      expect(keycloakProvider?.clientId).toBe('mosaic');
      expect(keycloakProvider?.discoveryUrl).toBe(
        'https://auth.example.com/realms/myrealm/.well-known/openid-configuration',
      );
      expect(keycloakProvider?.scopes).toEqual(['openid', 'email', 'profile']);
    });

    it('supports deriving the Keycloak issuer from KEYCLOAK_URL and KEYCLOAK_REALM', () => {
      process.env['KEYCLOAK_CLIENT_ID'] = 'mosaic';
      process.env['KEYCLOAK_CLIENT_SECRET'] = 'secret123';
      process.env['KEYCLOAK_URL'] = 'https://auth.example.com/';
      process.env['KEYCLOAK_REALM'] = 'myrealm';

      const providers = buildOAuthProviders();
      const keycloakProvider = providers.find((p) => p.providerId === 'keycloak');

      expect(keycloakProvider?.discoveryUrl).toBe(
        'https://auth.example.com/realms/myrealm/.well-known/openid-configuration',
      );
    });

    it('throws when Keycloak is partially configured', () => {
      process.env['KEYCLOAK_CLIENT_ID'] = 'mosaic';
      process.env['KEYCLOAK_CLIENT_SECRET'] = 'secret123';

      expect(() => buildOAuthProviders()).toThrow(
        '@mosaicstack/auth: Keycloak SSO requires KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET, KEYCLOAK_ISSUER.',
      );
    });

    it('excludes keycloak provider when Keycloak is not configured', () => {
      const providers = buildOAuthProviders();
      const keycloakProvider = providers.find((p) => p.providerId === 'keycloak');
      expect(keycloakProvider).toBeUndefined();
    });
  });

  describe('Authentik', () => {
    it('includes authentik provider when all required env vars are set', () => {
      process.env['AUTHENTIK_CLIENT_ID'] = 'authentik-client';
      process.env['AUTHENTIK_CLIENT_SECRET'] = 'authentik-secret';
      process.env['AUTHENTIK_ISSUER'] = 'https://auth.example.com/application/o/mosaic/';

      const providers = buildOAuthProviders();
      const authentik = providers.find((p) => p.providerId === 'authentik');

      expect(authentik).toBeDefined();
      expect(authentik?.clientId).toBe('authentik-client');
      expect(authentik?.issuer).toBe('https://auth.example.com/application/o/mosaic');
      expect(authentik?.discoveryUrl).toBe(
        'https://auth.example.com/application/o/mosaic/.well-known/openid-configuration',
      );
    });

    it('throws when Authentik is partially configured', () => {
      process.env['AUTHENTIK_CLIENT_ID'] = 'authentik-client';

      expect(() => buildOAuthProviders()).toThrow(
        '@mosaicstack/auth: Authentik SSO requires AUTHENTIK_ISSUER, AUTHENTIK_CLIENT_ID, AUTHENTIK_CLIENT_SECRET.',
      );
    });

    it('excludes authentik provider when Authentik is not configured', () => {
      const providers = buildOAuthProviders();
      const authentik = providers.find((p) => p.providerId === 'authentik');
      expect(authentik).toBeUndefined();
    });
  });

  it('registers all three providers when all env vars are set', () => {
    process.env['AUTHENTIK_CLIENT_ID'] = 'a-id';
    process.env['AUTHENTIK_CLIENT_SECRET'] = 'a-secret';
    process.env['AUTHENTIK_ISSUER'] = 'https://auth.example.com/application/o/mosaic';
    process.env['WORKOS_CLIENT_ID'] = 'w-id';
    process.env['WORKOS_CLIENT_SECRET'] = 'w-secret';
    process.env['WORKOS_ISSUER'] = 'https://example.authkit.app';
    process.env['KEYCLOAK_CLIENT_ID'] = 'k-id';
    process.env['KEYCLOAK_CLIENT_SECRET'] = 'k-secret';
    process.env['KEYCLOAK_ISSUER'] = 'https://kc.example.com/realms/test';

    const providers = buildOAuthProviders();
    expect(providers).toHaveLength(3);
    const ids = providers.map((p) => p.providerId);
    expect(ids).toContain('authentik');
    expect(ids).toContain('workos');
    expect(ids).toContain('keycloak');
  });
});
