package com.airlock.iam.oauth2.domain.service;

import com.airlock.iam.authentication.api.domain.service.username.IdentificationResult;
import com.airlock.iam.authentication.api.domain.service.username.IdentityProvidingService;
import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.common.api.domain.model.store.user.InsertingUserStore;
import com.airlock.iam.common.api.domain.model.user.ProvidedUsername;
import com.airlock.iam.common.domain.model.accountlink.AccountSub;
import com.airlock.iam.common.domain.model.provider.ProviderId;
import com.airlock.iam.login.api.domain.model.oauth2.OAuth2Credential;
import com.airlock.iam.oauth2.domain.model.accountregistration.AccountRegistrationContextDataExtractor;
import com.airlock.iam.oauth2.domain.model.accountregistration.AccountRegistrationUserFactory;
import com.airlock.iam.oauth2.domain.service.accountlinking.AccountLinkAccountInfoProvider;
import com.airlock.iam.oauth2.domain.service.accountlinking.AccountLinkRepository;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Provider;
import java.util.Optional;

public class OAuth2AccountRegistrationService {
   private final AccountRegistrationUserFactory accountRegistrationUserFactory;
   private final AccountLinkAccountInfoProvider accountInfoProvider;
   private final Optional<AccountLinkRepository> accountLinkRepository;
   private final InsertingUserStore userStore;
   private final ProviderId providerId;
   private final Provider<IdentityProvidingService> identificationService;

   public OAuth2AccountRegistrationService(
      AccountRegistrationUserFactory accountRegistrationUserFactory,
      AccountLinkAccountInfoProvider accountInfoProvider,
      Optional<AccountLinkRepository> accountLinkRepository,
      InsertingUserStore userStore,
      ProviderId providerId,
      Provider<IdentityProvidingService> identificationService
   ) {
      this.accountRegistrationUserFactory = accountRegistrationUserFactory;
      this.accountInfoProvider = accountInfoProvider;
      this.accountLinkRepository = accountLinkRepository;
      this.userStore = userStore;
      this.providerId = providerId;
      this.identificationService = identificationService;
   }

   public AutoRegisteredUser createAutoRegisteredUser(OAuth2Credential credential) {
      try {
         return AutoRegisteredUser.builder().credential(credential).persistentUser(this.accountRegistrationUserFactory.createUser(credential)).build();
      } catch (AccountRegistrationContextDataExtractor.MandatoryContextDataMissingException e) {
         throw new FailureException("Failed to create auto registered user.", e);
      }
   }

   public IdentificationResult persistAndLinkAutoRegisteredUser(AutoRegisteredUser autoRegisteredUser) {
      this.userStore.insert(autoRegisteredUser.getPersistentUser());
      this.accountLinkRepository
         .orElseThrow()
         .persistAndLoadAccountLink(
            autoRegisteredUser.getPersistentUser().getName(),
            this.providerId,
            AccountSub.accountSub(autoRegisteredUser.getCredential().getUsername()),
            this.accountInfoProvider.accountInfo(autoRegisteredUser.getCredential())
         );
      return ((IdentityProvidingService)this.identificationService.get())
         .tryIdentify(ProvidedUsername.providedUsernameOf(autoRegisteredUser.getPersistentUser().getName()));
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class OAuth2AccountRegistrationServiceBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private AccountRegistrationUserFactory accountRegistrationUserFactory;
      @SuppressFBWarnings(justification = "generated code")
      private AccountLinkAccountInfoProvider accountInfoProvider;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<AccountLinkRepository> accountLinkRepository;
      @SuppressFBWarnings(justification = "generated code")
      private InsertingUserStore userStore;
      @SuppressFBWarnings(justification = "generated code")
      private ProviderId providerId;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<IdentityProvidingService> identificationService;

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

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccountRegistrationService.OAuth2AccountRegistrationServiceBuilder accountRegistrationUserFactory(
         AccountRegistrationUserFactory accountRegistrationUserFactory
      ) {
         this.accountRegistrationUserFactory = accountRegistrationUserFactory;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccountRegistrationService.OAuth2AccountRegistrationServiceBuilder accountInfoProvider(AccountLinkAccountInfoProvider accountInfoProvider) {
         this.accountInfoProvider = accountInfoProvider;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccountRegistrationService.OAuth2AccountRegistrationServiceBuilder accountLinkRepository(
         Optional<AccountLinkRepository> accountLinkRepository
      ) {
         this.accountLinkRepository = accountLinkRepository;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccountRegistrationService.OAuth2AccountRegistrationServiceBuilder userStore(InsertingUserStore userStore) {
         this.userStore = userStore;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccountRegistrationService.OAuth2AccountRegistrationServiceBuilder providerId(ProviderId providerId) {
         this.providerId = providerId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccountRegistrationService.OAuth2AccountRegistrationServiceBuilder identificationService(
         Provider<IdentityProvidingService> identificationService
      ) {
         this.identificationService = identificationService;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccountRegistrationService build() {
         return new OAuth2AccountRegistrationService(
            this.accountRegistrationUserFactory,
            this.accountInfoProvider,
            this.accountLinkRepository,
            this.userStore,
            this.providerId,
            this.identificationService
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OAuth2AccountRegistrationService.OAuth2AccountRegistrationServiceBuilder(accountRegistrationUserFactory="
            + this.accountRegistrationUserFactory
            + ", accountInfoProvider="
            + this.accountInfoProvider
            + ", accountLinkRepository="
            + this.accountLinkRepository
            + ", userStore="
            + this.userStore
            + ", providerId="
            + this.providerId
            + ", identificationService="
            + this.identificationService
            + ")";
      }
   }
}
