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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.base.api.domain.model.store.query.Restriction;
import com.airlock.iam.common.api.domain.model.store.user.UserStore;
import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.login.api.domain.model.oauth2.OAuth2Credential;
import com.google.common.collect.ImmutableList;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.util.List;
import java.util.Optional;

public final class UserLookupRepository {
   private final UserStore userStore;
   @Nullable
   private final String matchingContextDataField;

   public Optional<PersistentUser> findMatchingUser(OAuth2Credential credential) {
      List<String> matchingUsers = this.matchingUsers(credential);
      if (matchingUsers.size() > 1) {
         throw new FailureException("Error finding matching user. Multiple users with the same context data value found.");
      } else {
         return !matchingUsers.isEmpty() ? this.userStore.findByIdentity(matchingUsers.getFirst()) : Optional.empty();
      }
   }

   private List<String> matchingUsers(OAuth2Credential credential) {
      Optional<String> linkContextDataField = this.linkContextDataFieldValue(credential);
      if (linkContextDataField.isEmpty()) {
         return ImmutableList.of();
      }

      Restriction userWithMatchingContextDataValue = this.userStore
         .schema()
         .customFields()
         .string(this.matchingContextDataField)
         .equal(linkContextDataField.get());
      return this.userStore.find().where(userWithMatchingContextDataValue).select(this.userStore.schema().identity().selector()).list();
   }

   private Optional<String> linkContextDataFieldValue(OAuth2Credential credential) {
      if (this.matchingContextDataField != null) {
         Object attributeValue = credential.getAttributeValue(this.matchingContextDataField);
         if (attributeValue != null) {
            if (attributeValue instanceof String) {
               return Optional.of((String)attributeValue);
            }

            throw new ViolationException("Linking context data field from provider can not be compared.");
         }
      }

      return Optional.empty();
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public UserLookupRepository(UserStore userStore, @Nullable String matchingContextDataField) {
      this.userStore = userStore;
      this.matchingContextDataField = matchingContextDataField;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class UserLookupRepositoryBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private UserStore userStore;
      @SuppressFBWarnings(justification = "generated code")
      private String matchingContextDataField;

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

      @SuppressFBWarnings(justification = "generated code")
      public UserLookupRepository.UserLookupRepositoryBuilder userStore(UserStore userStore) {
         this.userStore = userStore;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserLookupRepository.UserLookupRepositoryBuilder matchingContextDataField(@Nullable String matchingContextDataField) {
         this.matchingContextDataField = matchingContextDataField;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserLookupRepository build() {
         return new UserLookupRepository(this.userStore, this.matchingContextDataField);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "UserLookupRepository.UserLookupRepositoryBuilder(userStore="
            + this.userStore
            + ", matchingContextDataField="
            + this.matchingContextDataField
            + ")";
      }
   }
}
