package com.airlock.iam.authentication.application.service;

import com.airlock.iam.authentication.application.service.targetapp.TargetApplicationRepository;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.flow.ApplicationId;
import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.shared.application.service.AccessCall;
import com.airlock.iam.flow.shared.domain.model.step.access.AccessFlowStep;
import com.airlock.iam.login.api.domain.model.location.ForwardLocation;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

public final class ApplicationAccessService {
   private static final ELogger LOG = ELoggerFactory.getLogger(ApplicationAccessService.class);
   private final Provider<InternalApplicationAccessService> internalService;
   private final Provider<TargetApplicationRepository> repository;
   private final Provider<AccessCall> accessCall;

   @Inject
   public ApplicationAccessService(
      Provider<TargetApplicationRepository> repository, Provider<InternalApplicationAccessService> internalService, Provider<AccessCall> accessCall
   ) {
      this.repository = repository;
      this.internalService = internalService;
      this.accessCall = accessCall;
   }

   public FlowResult accessDefaultApplication(Optional<ForwardLocation> forwardLocation) {
      return this.callAccessMethodIfNecessary(
         ((InternalApplicationAccessService)this.internalService.get())
            .access(((TargetApplicationRepository)this.repository.get()).getDefaultApplication(), forwardLocation)
      );
   }

   public FlowResult startNewFlowForDefaultApplication(InternalApplicationAccessService.FlowSessionUpdater flowSessionUpdater) {
      return ((InternalApplicationAccessService)this.internalService.get())
         .startNewFlow(((TargetApplicationRepository)this.repository.get()).getDefaultApplication(), flowSessionUpdater);
   }

   public FlowResult access(ApplicationId applicationId, Optional<ForwardLocation> forwardLocation) {
      return this.callAccessMethodIfNecessary(
         ((InternalApplicationAccessService)this.internalService.get())
            .access(((TargetApplicationRepository)this.repository.get()).getByApplicationId(applicationId), forwardLocation)
      );
   }

   public FlowResult accessOneShot(ApplicationId applicationId) {
      return this.callAccessMethodIfNecessary(
         ((InternalApplicationAccessService)this.internalService.get())
            .accessOneShot(((TargetApplicationRepository)this.repository.get()).getOneShotApplicationById(applicationId))
      );
   }

   public FlowResult startNewFlowForApplication(ApplicationId applicationId, InternalApplicationAccessService.FlowSessionUpdater flowSessionUpdater) {
      return ((InternalApplicationAccessService)this.internalService.get())
         .startNewFlow(((TargetApplicationRepository)this.repository.get()).getByApplicationId(applicationId), flowSessionUpdater);
   }

   public FlowResult startNewFlowOrReuseIfSameLocationUri(ForwardLocation location) {
      return this.callAccessMethodIfNecessary(
         ((InternalApplicationAccessService)this.internalService.get())
            .startNewFlowOrReuseIfSameLocationUri(((TargetApplicationRepository)this.repository.get()).getByForwardURIOrDefault(location), location)
      );
   }

   private FlowResult callAccessMethodIfNecessary(FlowResult result) {
      if (result.nextStep().isPresent() && result.nextStep().get().name().equals(AccessFlowStep.AccessStepActions.ACCESS_CALL_REQUIRED.name())) {
         LOG.debug("Automatically calling access() method on current step.");
         return ((AccessCall)this.accessCall.get()).access();
      } else {
         return result;
      }
   }
}
