package com.airlock.iam.oauth2.infrastructure.service;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.oauth2.application.configuration.OAuth2AuthorizationServerConfigSpec;
import com.airlock.iam.oauth2.domain.model.session.OpenIDProviderUserAgentState;
import com.airlock.iam.oauth2.domain.model.session.OpenIdConnectSessionStateCalculator;
import com.airlock.iam.oauth2.domain.model.settings.OAuth2OidcAuthorizationCodeGrantSettings;
import com.airlock.iam.oauth2.domain.model.settings.OpenIdConnectIdTokenSettings;
import com.airlock.iam.oauth2.domain.service.persistence.OAuth2AuthorizationCodeGenerator;
import com.airlock.iam.oauth2.domain.service.persistence.OAuth2AuthorizationCodeGeneratorImpl;
import com.airlock.iam.oauth2.domain.service.token.OpenIdConnectSignedIdTokenFactory;
import java.util.Optional;

public final class OAuth2AuthorizationCodeServiceFactory {
   private OAuth2AuthorizationCodeServiceFactory() {
   }

   public static OAuth2AuthorizationCodeGenerator authorizationCodeGenerator(
      OAuth2OidcAuthorizationCodeGrantSettings grantConfig, OAuth2AuthorizationServerConfigSpec asConfig
   ) {
      return OAuth2AuthorizationCodeGeneratorImpl.builder()
         .tokenDataProvider(asConfig.getTokenDataProvider())
         .randomTokenGenerator(asConfig.getRandomTokenGenerator())
         .passwordHash(asConfig.getTokenHash())
         .authorizationCodeExpiresIn(grantConfig.getAuthorizationCodeExpiresIn())
         .build();
   }

   public static OpenIdConnectCodeGrantRedirectUriGeneratorImpl oidcCodeGrantUriGenerator(
      OAuth2AuthorizationServerConfigSpec asConfig, Optional<OpenIDProviderUserAgentState> openIDProviderUserAgentState, ELogger integrationLogger
   ) {
      return new OpenIdConnectCodeGrantRedirectUriGeneratorImpl(
         integrationLogger,
         asConfig.tokenRepresentation(),
         new OpenIdConnectSessionStateCalculator(openIDProviderUserAgentState),
         hybridFlowSignedIdTokenFactory(asConfig)
      );
   }

   public static OAuth2CodeGrantRedirectUriGeneratorImpl oauth2CodeGrantUriGenerator(OAuth2AuthorizationServerConfigSpec asConfig, ELogger integrationLogger) {
      return new OAuth2CodeGrantRedirectUriGeneratorImpl(integrationLogger, asConfig.tokenRepresentation());
   }

   private static OpenIdConnectSignedIdTokenFactory hybridFlowSignedIdTokenFactory(OAuth2AuthorizationServerConfigSpec asConfig) {
      return new OpenIdConnectSignedIdTokenFactoryImpl(
         () -> asConfig::getIntegrationLogger,
         () -> hybridFlowIdToken(asConfig).signer(),
         () -> new OpenIdConnectSignedIdTokenFactorySettings(asConfig.getIssuerId().orElseThrow())
      );
   }

   private static OpenIdConnectIdTokenSettings hybridFlowIdToken(OAuth2AuthorizationServerConfigSpec asConfig) {
      return asConfig.getHybridFlowSettings()
         .orElseThrow(() -> new FailureException("OpenID Connect Hybrid Flow not configured"))
         .getIdTokenSettings()
         .orElseThrow(() -> new FailureException("OpenID Connect Hybrid Flow ID Token is not configured"));
   }
}
