package com.airlock.iam.flow.shared.domain.model.airlock2fa;

import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FADeviceId;
import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.api.domain.model.validation.Validator;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FADeviceDisplayName;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FALogData;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAUser;
import com.airlock.iam.factor.domain.service.airlock2fa.Airlock2FAAuthApiDeviceManagementService;
import com.airlock.iam.factor.domain.service.airlock2fa.Airlock2FAUserInformationService;
import com.airlock.iam.flow.api.domain.model.step.GenericStepError;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.flow.domain.model.ValidationStepResultData;
import com.airlock.iam.flow.shared.application.configuration.airlock2fa.Airlock2FADeviceEditStepConfig;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import com.airlock.iam.log.api.signing.SigningLogger;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;

@LogicForConfig(Airlock2FADeviceEditStepConfig.class)
public class Airlock2FADeviceEditStepImpl implements Airlock2FADeviceEditStep {
   private static final ELogger LOG = ELoggerFactory.getLogger(Airlock2FADeviceEditStepImpl.class);
   private final Validator<Airlock2FADeviceEditData> deviceEditValidator;
   private final Airlock2FAAuthApiDeviceManagementService deviceManagementService;
   private final Provider<Airlock2FACurrentDeviceIdFlowAttribute> currentDeviceId;
   private final Provider<Airlock2FAUserInformationService> userInformationService;
   private final SigningLogger signingLogger;
   private final HelpdeskLogger helpdeskLogger;
   private final Provider<Airlock2FAUser> user;

   @Inject
   public Airlock2FADeviceEditStepImpl(
      Validator<Airlock2FADeviceEditData> deviceEditValidator,
      Airlock2FAAuthApiDeviceManagementService deviceManagementService,
      Provider<Airlock2FACurrentDeviceIdFlowAttribute> currentDeviceId,
      Provider<Airlock2FAUserInformationService> userInformationService,
      SigningLogger signingLogger,
      HelpdeskLogger helpdeskLogger,
      Provider<Airlock2FAUser> user
   ) {
      this.deviceEditValidator = deviceEditValidator;
      this.deviceManagementService = deviceManagementService;
      this.currentDeviceId = currentDeviceId;
      this.userInformationService = userInformationService;
      this.signingLogger = signingLogger;
      this.helpdeskLogger = helpdeskLogger;
      this.user = user;
   }

   @Override
   public StepResult initialize() {
      return this.stepResultFactory()
         .stay(Airlock2FADeviceEditStep.Airlock2FADeviceEditStepActions.AIRLOCK_2FA_DEVICE_EDIT_POSSIBLE)
         .addAdditionalAttribute("displayName", this.currentDeviceDisplayNameValue().getValue())
         .build();
   }

   @Override
   public StepResult retrieveCurrentDisplayName() {
      return this.stepResultFactory()
         .stay(Airlock2FADeviceEditStep.Airlock2FADeviceEditStepActions.AIRLOCK_2FA_DEVICE_EDIT_POSSIBLE)
         .resultData(
            Airlock2FADeviceEditStep.Airlock2FADeviceCurrentData.airlock2FADeviceData(
               ((Airlock2FACurrentDeviceIdFlowAttribute)this.currentDeviceId.get()).getDeviceId(), this.currentDeviceDisplayNameValue()
            )
         )
         .build();
   }

   private Airlock2FADeviceDisplayName currentDeviceDisplayNameValue() {
      return ((Airlock2FAUserInformationService)this.userInformationService.get())
         .getUser(((Airlock2FAUser)this.user.get()).getAccountId())
         .map(userInfo -> userInfo.deviceDisplayName(((Airlock2FACurrentDeviceIdFlowAttribute)this.currentDeviceId.get()).getDeviceId()))
         .orElseThrow(() -> new FailureException(String.format("User with Airlock 2FA ID '%s' not found", ((Airlock2FAUser)this.user.get()).getAccountId())));
   }

   @Override
   public StepResult registerDisplayNameChange(String providedDisplayName) {
      Airlock2FADeviceId deviceId = ((Airlock2FACurrentDeviceIdFlowAttribute)this.currentDeviceId.get()).getDeviceId();
      ValidationResult validationResult = this.deviceEditValidator
         .validate(Airlock2FADeviceEditData.builder().deviceId(deviceId).displayName(providedDisplayName).build());
      if (!validationResult.isValid()) {
         LOG.debug(String.format("Provided display name %s is not valid", LogUtil.quote(providedDisplayName)));
         return this.stepResultFactory()
            .failRetry(GenericStepError.VALIDATION_FAILED, Airlock2FADeviceEditStep.Airlock2FADeviceEditStepActions.AIRLOCK_2FA_DEVICE_EDIT_POSSIBLE)
            .addAdditionalAttribute("displayName", this.currentDeviceDisplayNameValue().getValue())
            .resultData(new ValidationStepResultData(validationResult))
            .build();
      } else {
         this.deviceManagementService.modifyDeviceDisplayName(deviceId, Airlock2FADeviceDisplayName.airlock2FADeviceDisplayName(providedDisplayName));
         Airlock2FALogData logData = Airlock2FALogData.airlock2FALogData(((Airlock2FAUser)this.user.get()).getAccountId()).withDeviceId(deviceId);
         this.signingLogger
            .log(
               "User %s changed display name of Airlock 2FA device to %s %s",
               new Object[]{LogUtil.quote(((Airlock2FAUser)this.user.get()).getIamUserId()), LogUtil.quote(providedDisplayName), logData}
            );
         this.helpdeskLogger
            .log(
               ((Airlock2FAUser)this.user.get()).getIamUserId(),
               "Changed display name of Airlock 2FA device to %s %s",
               new Object[]{LogUtil.quote(providedDisplayName), logData}
            );
         return this.stepResultFactory()
            .stay(Airlock2FADeviceEditStep.Airlock2FADeviceEditStepActions.AIRLOCK_2FA_DEVICE_EDIT_POSSIBLE)
            .addAdditionalAttribute("displayName", providedDisplayName)
            .build();
      }
   }

   @Override
   public StepResult validateAndContinue() {
      return this.stepResultFactory().success().build();
   }

   @Override
   public String getDescription() {
      return "Airlock 2FA Device Edit Step";
   }

   @SuppressFBWarnings(justification = "generated code")
   public static Airlock2FADeviceEditStepImpl.Airlock2FADeviceEditStepImplBuilder builder() {
      return new Airlock2FADeviceEditStepImpl.Airlock2FADeviceEditStepImplBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class Airlock2FADeviceEditStepImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Validator<Airlock2FADeviceEditData> deviceEditValidator;
      @SuppressFBWarnings(justification = "generated code")
      private Airlock2FAAuthApiDeviceManagementService deviceManagementService;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<Airlock2FACurrentDeviceIdFlowAttribute> currentDeviceId;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<Airlock2FAUserInformationService> userInformationService;
      @SuppressFBWarnings(justification = "generated code")
      private SigningLogger signingLogger;
      @SuppressFBWarnings(justification = "generated code")
      private HelpdeskLogger helpdeskLogger;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<Airlock2FAUser> user;

      @SuppressFBWarnings(justification = "generated code")
      Airlock2FADeviceEditStepImplBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FADeviceEditStepImpl.Airlock2FADeviceEditStepImplBuilder deviceEditValidator(Validator<Airlock2FADeviceEditData> deviceEditValidator) {
         this.deviceEditValidator = deviceEditValidator;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FADeviceEditStepImpl.Airlock2FADeviceEditStepImplBuilder deviceManagementService(
         Airlock2FAAuthApiDeviceManagementService deviceManagementService
      ) {
         this.deviceManagementService = deviceManagementService;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FADeviceEditStepImpl.Airlock2FADeviceEditStepImplBuilder currentDeviceId(Provider<Airlock2FACurrentDeviceIdFlowAttribute> currentDeviceId) {
         this.currentDeviceId = currentDeviceId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FADeviceEditStepImpl.Airlock2FADeviceEditStepImplBuilder userInformationService(
         Provider<Airlock2FAUserInformationService> userInformationService
      ) {
         this.userInformationService = userInformationService;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FADeviceEditStepImpl.Airlock2FADeviceEditStepImplBuilder signingLogger(SigningLogger signingLogger) {
         this.signingLogger = signingLogger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FADeviceEditStepImpl.Airlock2FADeviceEditStepImplBuilder helpdeskLogger(HelpdeskLogger helpdeskLogger) {
         this.helpdeskLogger = helpdeskLogger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FADeviceEditStepImpl.Airlock2FADeviceEditStepImplBuilder user(Provider<Airlock2FAUser> user) {
         this.user = user;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FADeviceEditStepImpl build() {
         return new Airlock2FADeviceEditStepImpl(
            this.deviceEditValidator,
            this.deviceManagementService,
            this.currentDeviceId,
            this.userInformationService,
            this.signingLogger,
            this.helpdeskLogger,
            this.user
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "Airlock2FADeviceEditStepImpl.Airlock2FADeviceEditStepImplBuilder(deviceEditValidator="
            + this.deviceEditValidator
            + ", deviceManagementService="
            + this.deviceManagementService
            + ", currentDeviceId="
            + this.currentDeviceId
            + ", userInformationService="
            + this.userInformationService
            + ", signingLogger="
            + this.signingLogger
            + ", helpdeskLogger="
            + this.helpdeskLogger
            + ", user="
            + this.user
            + ")";
      }
   }
}
