package com.airlock.iam.authentication.domain.model.rememberme;

import com.airlock.iam.authentication.application.configuration.rememberme.RememberMeTokenGeneratingStepConfig;
import com.airlock.iam.authentication.domain.service.rememberme.RememberMeTokenGenerationService;
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.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
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.rememberme.RememberMeSecret;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import com.airlock.iam.log.api.signing.SigningLogger;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

@LogicForConfig(RememberMeTokenGeneratingStepConfig.class)
public class RememberMeTokenGeneratingStep implements FlowStep {
   private final Provider<RememberMeTokenGenerationService> tokenGenerationService;
   private final Provider<PrincipalInfo> user;
   private final Provider<RememberMeResponse> response;
   private final Provider<RememberMeCookie.RememberMeCookieBuilder> preconfiguredCookieBuilder;
   private final Provider<HelpdeskLogger> helpdeskLogger;
   private final Provider<SigningLogger> signingLogger;

   @Inject
   public RememberMeTokenGeneratingStep(
      Provider<RememberMeTokenGenerationService> tokenGenerationService,
      Provider<PrincipalInfo> user,
      Provider<RememberMeResponse> response,
      Provider<RememberMeCookie.RememberMeCookieBuilder> preconfiguredCookieBuilder,
      Provider<HelpdeskLogger> helpdeskLogger,
      Provider<SigningLogger> signingLogger
   ) {
      this.tokenGenerationService = tokenGenerationService;
      this.user = user;
      this.response = response;
      this.preconfiguredCookieBuilder = preconfiguredCookieBuilder;
      this.helpdeskLogger = helpdeskLogger;
      this.signingLogger = signingLogger;
   }

   @Override
   public StepResult initialize() {
      RememberMeTokenResult result = ((RememberMeTokenGenerationService)this.tokenGenerationService.get())
         .createToken(this.userId().orElseThrow(() -> new FailureException("No known user identity")));
      this.setRememberMeCookieInResponse(result.secret());
      this.logGeneratedToken(result);
      return this.stepResultFactory().success().build();
   }

   @Override
   public String getDescription() {
      return "Remember-Me Token Generating Step";
   }

   private void setRememberMeCookieInResponse(RememberMeSecret secret) {
      RememberMeCookie cookie = ((RememberMeCookie.RememberMeCookieBuilder)this.preconfiguredCookieBuilder.get()).value(secret.base64Url()).build();
      ((RememberMeResponse)this.response.get()).addRememberMeCookie(cookie);
   }

   private void logGeneratedToken(RememberMeTokenResult result) {
      UserId userId = this.userId().orElseThrow();
      String message = String.format(
         "Issued new Remember-Me cookie for user %s with ID %s and hash %s",
         LogUtil.logSafeQuote(userId),
         LogUtil.quote(result.token().getId()),
         LogUtil.quote(result.secret().hash())
      );
      ((SigningLogger)this.signingLogger.get()).log(message);
      ((HelpdeskLogger)this.helpdeskLogger.get()).log(userId, message);
   }

   private Optional<UserId> userId() {
      return ((PrincipalInfo)this.user.get()).getKnownIdentity().map(UserIdentity::id);
   }
}
