package com.airlock.iam.techclientreg.domain.model.step;

import com.airlock.iam.base.api.domain.model.identity.Identity;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.session.FlowAttributeRepository;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.techclient.api.domain.model.TechnicalClient;
import com.airlock.iam.techclient.api.domain.model.oauth2.OAuth2TechnicalClientMetadata;
import com.airlock.iam.techclient.auth.domain.model.oauth2.OAuth2TechClient;
import com.airlock.iam.techclient.domain.model.RegisterableTechnicalClient;
import com.airlock.iam.techclientreg.api.domain.model.oauth2.OAuth2ClientRegistrationRequest;
import com.airlock.iam.techclientreg.application.configuration.step.OAuth2ClientRegistrationStepConfig;
import com.airlock.iam.techclientreg.domain.model.flow.RegisteringTechClientFlowAttribute;
import com.airlock.iam.techclientreg.domain.service.OAuth2ClientRegistrationLogic;
import com.airlock.iam.techclientreg.domain.service.OAuth2TechClientRegistrationResponseDataMapper;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

@LogicForConfig(OAuth2ClientRegistrationStepConfig.class)
public class OAuth2ClientRegistrationStepImpl implements OAuth2ClientRegistrationStep {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2ClientRegistrationStepImpl.class);
   private final Provider<FlowAttributeRepository> flowAttributes;
   private final Provider<OAuth2ClientRegistrationLogic> registrationService;
   private final Provider<OAuth2ClientRegistrationStepImpl.OAuth2ClientRegistrationStepSettings> settings;

   @Inject
   public OAuth2ClientRegistrationStepImpl(
      Provider<FlowAttributeRepository> flowAttributes,
      Provider<OAuth2ClientRegistrationLogic> registrationService,
      Provider<OAuth2ClientRegistrationStepImpl.OAuth2ClientRegistrationStepSettings> settings
   ) {
      this.flowAttributes = flowAttributes;
      this.registrationService = registrationService;
      this.settings = settings;
   }

   @Override
   public StepResult initialize() {
      return this.stepResultFactory().stay(OAuth2ClientRegistrationStep.RegistrationStepAction.OAUTH2_CLIENT_DATA_REQUIRED).build();
   }

   @Override
   public StepResult register(OAuth2ClientRegistrationRequest registrationRequest) {
      OAuth2ClientRegistrationLogic.OAuth2ClientRegistrationResponse result = ((OAuth2ClientRegistrationLogic)this.registrationService.get())
         .registerClient(registrationRequest);
      if (result.isSuccess()) {
         OAuth2ClientRegistrationResponseData resultData = this.mapToResultData(result.getClient());
         this.storeClientInFlowAttribute(result.getClient());
         return this.stepResultFactory().success().resultData(resultData).build();
      } else {
         LOG.info(
            String.format(
               "Dynamic Client Registration failed with error code '%s' and description '%s'.",
               result.getError().getErrorCode(),
               result.getError().getDescription()
            )
         );
         return this.stepResultFactory()
            .fail(OAuth2ClientRegistrationStep.RegistrationStepError.OAUTH2_CLIENT_REGISTRATION_FAILED)
            .resultData(result.getError())
            .build();
      }
   }

   private OAuth2ClientRegistrationResponseData mapToResultData(OAuth2TechClient client) {
      OAuth2TechnicalClientMetadata metadata = client.getMetadata();
      return OAuth2TechClientRegistrationResponseDataMapper.map(this.returnTechClientIdIfConfigured(client), metadata);
   }

   private Optional<Identity<TechnicalClient>> returnTechClientIdIfConfigured(OAuth2TechClient client) {
      return ((OAuth2ClientRegistrationStepImpl.OAuth2ClientRegistrationStepSettings)this.settings.get()).returnTechClientId()
         ? Optional.of(client.getId())
         : Optional.empty();
   }

   private void storeClientInFlowAttribute(RegisterableTechnicalClient client) {
      ((FlowAttributeRepository)this.flowAttributes.get()).putOrMergeAttribute(new RegisteringTechClientFlowAttribute(client));
   }

   @Override
   public String getDescription() {
      return "OAuth 2.0 Dynamic Client Registration Step";
   }

   public record OAuth2ClientRegistrationStepSettings(boolean returnTechClientId) {
   }
}
