package com.airlock.iam.authentication.domain.service.newdevice;

import com.airlock.iam.authentication.application.configuration.newdevice.LoginFromNewDeviceStepConfig;
import com.airlock.iam.authentication.domain.model.event.LoginFromNewDeviceEventFactory;
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.user.UserId;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.step.FlowStep;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.flow.shared.domain.model.newdevice.LoginFromNewDeviceKnownUsers;
import com.airlock.iam.flow.shared.domain.model.newdevice.LoginFromNewDeviceUserInfo;
import com.airlock.iam.flow.shared.domain.service.newdevice.LoginFromNewDeviceKnownUsersReader;
import com.airlock.iam.log.api.signing.SigningLogger;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

@LogicForConfig(LoginFromNewDeviceStepConfig.class)
public class LoginFromNewDeviceStep implements FlowStep {
   private static final ELogger LOG = ELoggerFactory.getLogger(LoginFromNewDeviceStep.class);
   private final Provider<PrincipalInfo> principalInfo;
   private final Provider<LoginFromNewDeviceKnownUsersReader> knownUsersReader;
   private final Provider<LoginFromNewDeviceKnownUsersWriter> knownUsersWriter;
   private final Provider<LoginFromNewDeviceEventFactory> eventFactory;
   private final Provider<SigningLogger> logger;

   @Inject
   public LoginFromNewDeviceStep(
      Provider<PrincipalInfo> principalInfo,
      Provider<LoginFromNewDeviceKnownUsersReader> knownUsersReader,
      Provider<LoginFromNewDeviceKnownUsersWriter> knownUsersWriter,
      Provider<LoginFromNewDeviceEventFactory> eventFactory,
      Provider<SigningLogger> logger
   ) {
      this.principalInfo = principalInfo;
      this.knownUsersReader = knownUsersReader;
      this.knownUsersWriter = knownUsersWriter;
      this.eventFactory = eventFactory;
      this.logger = logger;
   }

   @Override
   public StepResult initialize() {
      LoginFromNewDeviceKnownUsers knownUsers = ((LoginFromNewDeviceKnownUsersReader)this.knownUsersReader.get()).getValidKnownUsers();
      Optional<LoginFromNewDeviceUserInfo> userDeviceInfo = knownUsers.findUser(this.userId());
      if (userDeviceInfo.isEmpty()) {
         ((SigningLogger)this.logger.get()).log(String.format("User %s logged in from a new device.", this.userId().getValue()));
         ((LoginFromNewDeviceEventFactory)this.eventFactory.get()).publishLoggedInFromNewDeviceEvent(this.userId());
      } else {
         LOG.debug(String.format("User %s logged in with a device known since %s.", this.userId().getValue(), userDeviceInfo.get().createdAt().instant()));
      }

      ((LoginFromNewDeviceKnownUsersWriter)this.knownUsersWriter.get()).updateKnownUsers(knownUsers, this.userId());
      return this.stepResultFactory().success().build();
   }

   @Override
   public String getDescription() {
      return "Login From New Device Step";
   }

   private UserId userId() {
      return ((PrincipalInfo)this.principalInfo.get())
         .getKnownIdentity()
         .map(UserIdentity::id)
         .orElseThrow(() -> new FailureException("User must be known for this step."));
   }
}
