package com.airlock.iam.oauth2.domain.model.accountlinking;

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.domain.model.accountlink.AccountInfo;
import com.airlock.iam.common.domain.model.accountlink.AccountSub;
import com.airlock.iam.common.domain.model.accountlink.persistence.AccountLinkData;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.redflag.RedFlag;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.flow.shared.api.domain.model.session.UserDependentFlowSession;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import com.airlock.iam.oauth2.application.configuration.accountlinking.MissingAccountLinkStepConfig;
import com.airlock.iam.oauth2.domain.service.accountlinking.AccountLinkRepository;
import jakarta.inject.Inject;
import jakarta.inject.Provider;

@LogicForConfig(MissingAccountLinkStepConfig.class)
public class MissingAccountLinkStepImpl implements MissingAccountLinkStep {
   private static final ELogger LOG = ELoggerFactory.getLogger(MissingAccountLinkStepImpl.class);
   private final HelpdeskLogger helpdeskLogger;
   private final Provider<UserDependentFlowSession> flowSession;
   private final RedFlag redFlag;
   private final AccountLinkRepository repository;

   @Inject
   public MissingAccountLinkStepImpl(
      HelpdeskLogger helpdeskLogger, Provider<UserDependentFlowSession> flowSession, RedFlag redFlag, AccountLinkRepository repository
   ) {
      this.helpdeskLogger = helpdeskLogger;
      this.flowSession = flowSession;
      this.redFlag = redFlag;
      this.repository = repository;
   }

   @Override
   public StepResult initialize() {
      UserId userId = this.userId();
      OAuth2MissingAccountLinkFlowSessionAttribute attribute = this.missingAccountLinkFlowSessionAttribute();
      String username = userId.getValue();
      AccountSub accountSub = attribute.getAccountSub();
      AccountInfo accountInfo = attribute.getAccountInfo();
      AccountLinkData accountLinkData = this.repository.persistAndLoadAccountLink(username, attribute.getProviderId(), accountSub, accountInfo);
      LOG.info(
         String.format(
            "Linked user %s with sub %s of provider %s",
            LogUtil.quote(username),
            LogUtil.quote(accountLinkData.getAccountSub()),
            LogUtil.quote(accountLinkData.getProviderId())
         )
      );
      this.helpdeskLogger
         .log(
            userId,
            "Linked user with sub %s of provider %s",
            new Object[]{LogUtil.quote(accountLinkData.getAccountSub()), LogUtil.quote(accountLinkData.getProviderId())}
         );
      ((UserDependentFlowSession)this.flowSession.get()).takeDownRedFlag(this.redFlag);
      return this.stepResultFactory().success().build();
   }

   private OAuth2MissingAccountLinkFlowSessionAttribute missingAccountLinkFlowSessionAttribute() {
      return ((UserDependentFlowSession)this.flowSession.get())
         .flowAttributes()
         .tryGetAttribute(OAuth2MissingAccountLinkFlowSessionAttribute.class)
         .orElseThrow(() -> new FailureException("Missing account link attribute not found in session."));
   }

   private UserId userId() {
      return ((UserDependentFlowSession)this.flowSession.get())
         .getUserIdentity()
         .orElseThrow(() -> new FailureException("User must be known at this point"))
         .id();
   }

   @Override
   public String getDescription() {
      return "Missing Account Link Step";
   }
}
