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

import com.airlock.iam.base.misc.result.Result;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.oauth2.api.domain.model.clientauthentication.OAuth2ClientAuthentication;
import com.airlock.iam.oauth2.api.domain.model.tokenexchange.OAuth2TokenExchangeGrantRequest;
import com.airlock.iam.oauth2.domain.model.client.OAuth2Client;
import com.airlock.iam.oauth2.domain.model.logging.OAuth2LogTypes;
import com.airlock.iam.oauth2.domain.model.tokenexchange.OAuth2TokenExchangeErrorResponse;
import com.airlock.iam.oauth2.domain.model.tokenexchange.OAuth2TokenExchangeGrantAccessToken;
import com.airlock.iam.oauth2.domain.model.tokenexchange.rules.OAuth2TokenExchangeRule;
import com.airlock.iam.oauth2.domain.model.validation.OAuth2Error;
import com.airlock.iam.oauth2.domain.service.tokenexchange.OAuth2TokenExchangeTokenIssuingService;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;

public final class OAuth2TokenExchangeTokenIssuingServiceImpl implements OAuth2TokenExchangeTokenIssuingService {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2TokenExchangeTokenIssuingServiceImpl.class);
   private final Provider<List<OAuth2TokenExchangeRule>> tokenExchangeRules;

   @Inject
   public OAuth2TokenExchangeTokenIssuingServiceImpl(Provider<List<OAuth2TokenExchangeRule>> tokenExchangeRules) {
      this.tokenExchangeRules = tokenExchangeRules;
   }

   @Override
   public Result<OAuth2TokenExchangeGrantAccessToken, OAuth2TokenExchangeErrorResponse> issueAccessToken(
      OAuth2Client client, OAuth2ClientAuthentication authentication, OAuth2TokenExchangeGrantRequest grantRequest
   ) {
      return ((List)this.tokenExchangeRules.get())
         .stream()
         .filter(rule -> rule.canHandle(grantRequest))
         .findFirst()
         .map(rule -> rule.issueAccessToken(client, authentication, grantRequest))
         .orElseGet(() -> {
            LOG.info(OAuth2LogTypes.OAUTH2_MESSAGE, "Unable to issue access token: no matching token exchange rule found");
            return failure(OAuth2Error.INVALID_TARGET, "No matching token exchange rule for request", 400);
         });
   }

   private static Result<OAuth2TokenExchangeGrantAccessToken, OAuth2TokenExchangeErrorResponse> failure(OAuth2Error error, String description, int statusCode) {
      return Result.failure(OAuth2TokenExchangeErrorResponse.tokenExchangeErrorResponse(error, description, statusCode));
   }
}
