import crypto from 'crypto';

import { logger } from '@goodgo/logger';

import { OIDCDiscoveryDocument, OIDCTokenExchangeResponse } from '../../types/oidc.types';

/**
 * EN: OIDC Client Service for connecting to external OIDC providers
 * VI: Service OIDC Client để kết nối với các nhà cung cấp OIDC bên ngoài
 */
export class OIDCClientService {
  /**
   * EN: Discover OIDC provider
   * VI: Khám phá nhà cung cấp OIDC
   */
  async discoverProvider(issuerUrl: string): Promise<OIDCDiscoveryDocument> {
    try {
      const response = await fetch(`${issuerUrl}/.well-known/openid-configuration`);
      if (!response.ok) {
        throw new Error('Failed to discover provider');
      }
      return await response.json();
    } catch (error) {
      logger.error('OIDC provider discovery failed', { issuerUrl, error });
      throw error;
    }
  }

  /**
   * EN: Generate authorization URL with PKCE
   * VI: Tạo URL xác thực với PKCE
   */
  generateAuthorizationUrl(
    authorizationEndpoint: string,
    clientId: string,
    redirectUri: string,
    scopes: string[] = ['openid', 'email', 'profile']
  ): { url: string; codeVerifier: string; state: string } {
    // Generate PKCE values
    const codeVerifier = crypto.randomBytes(32).toString('base64url');
    const codeChallenge = crypto
      .createHash('sha256')
      .update(codeVerifier)
      .digest('base64url');
    
    const state = crypto.randomBytes(16).toString('hex');

    const params = new URLSearchParams({
      client_id: clientId,
      redirect_uri: redirectUri,
      response_type: 'code',
      scope: scopes.join(' '),
      code_challenge: codeChallenge,
      code_challenge_method: 'S256',
      state,
    });

    return {
      url: `${authorizationEndpoint}?${params.toString()}`,
      codeVerifier,
      state,
    };
  }

  /**
   * EN: Exchange code for tokens
   * VI: Đổi code lấy tokens
   */
  async exchangeCodeForTokens(
    tokenEndpoint: string,
    code: string,
    clientId: string,
    clientSecret: string,
    redirectUri: string,
    codeVerifier: string
  ): Promise<OIDCTokenExchangeResponse> {
    const response = await fetch(tokenEndpoint, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'authorization_code',
        code,
        redirect_uri: redirectUri,
        client_id: clientId,
        client_secret: clientSecret,
        code_verifier: codeVerifier,
      }),
    });

    if (!response.ok) {
      const errorText = await response.text();
      logger.error('OIDC token exchange failed', { status: response.status, error: errorText });
      throw new Error('Failed to exchange code for tokens');
    }

    const data = await response.json();
    return {
      access_token: data.access_token,
      refresh_token: data.refresh_token,
      id_token: data.id_token,
      token_type: data.token_type || 'Bearer',
      expires_in: data.expires_in || 3600,
    };
  }
}

export const oidcClientService = new OIDCClientService();
