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

import com.airlock.iam.common.domain.model.accountlink.AccountInfo;
import com.airlock.iam.common.domain.model.accountlink.AccountLinkDataToPersist;
import com.airlock.iam.common.domain.model.accountlink.AccountLinkId;
import com.airlock.iam.common.domain.model.accountlink.AccountSub;
import com.airlock.iam.common.domain.model.accountlink.persistence.AccountLinkData;
import com.airlock.iam.common.domain.model.provider.ProviderId;
import com.airlock.iam.common.domain.service.accountlink.persistence.AccountLinkPersister;
import com.airlock.iam.core.misc.authen.Authentee;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Predicate;
import lombok.NonNull;
import org.apache.commons.lang3.Strings;

public final class AccountLinkServiceImpl implements AccountLinkService {
   @NonNull
   private final AccountLinkPersister persister;
   @NonNull
   private final Authentee authentee;

   @Override
   public Collection<AccountLinkData> accountLinks() {
      return this.persister.findByUser(this.authentee.getName());
   }

   @Override
   public void link(ProviderId providerId, AccountSub accountSubject, AccountInfo accountInfo) {
      AccountLinkDataToPersist dataToPersist = AccountLinkDataToPersist.accountLinkDataToPersist(
         this.authentee.getName(), providerId.id(), accountSubject.sub(), accountInfo.info()
      );
      this.persister.persist(dataToPersist);
   }

   @Override
   public void unlink(ProviderId providerId) {
      Optional<AccountLinkData> data = this.accountLinks().stream().filter(matchingProvider(providerId)).findFirst();
      data.ifPresent(d -> this.persister.delete(AccountLinkId.accountLinkId(d.getId())));
   }

   @Override
   public boolean hasUserLinkForProvider(ProviderId providerId) {
      return this.persister.hasLinkForProvider(this.authentee.getName(), providerId);
   }

   @Override
   public boolean linkExists(ProviderId providerId, AccountSub accountSub) {
      return this.persister.findUnique(providerId, accountSub).isPresent();
   }

   private static Predicate<AccountLinkData> matchingProvider(ProviderId providerId) {
      return data -> Strings.CS.equals(data.getProviderId(), providerId.id());
   }

   @SuppressFBWarnings(justification = "generated code")
   AccountLinkServiceImpl(@NonNull AccountLinkPersister persister, @NonNull Authentee authentee) {
      if (persister == null) {
         throw new NullPointerException("persister is marked non-null but is null");
      }

      if (authentee == null) {
         throw new NullPointerException("authentee is marked non-null but is null");
      }

      this.persister = persister;
      this.authentee = authentee;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class AccountLinkServiceImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private AccountLinkPersister persister;
      @SuppressFBWarnings(justification = "generated code")
      private Authentee authentee;

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

      @SuppressFBWarnings(justification = "generated code")
      public AccountLinkServiceImpl.AccountLinkServiceImplBuilder persister(@NonNull AccountLinkPersister persister) {
         if (persister == null) {
            throw new NullPointerException("persister is marked non-null but is null");
         }

         this.persister = persister;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AccountLinkServiceImpl.AccountLinkServiceImplBuilder authentee(@NonNull Authentee authentee) {
         if (authentee == null) {
            throw new NullPointerException("authentee is marked non-null but is null");
         }

         this.authentee = authentee;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AccountLinkServiceImpl build() {
         return new AccountLinkServiceImpl(this.persister, this.authentee);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "AccountLinkServiceImpl.AccountLinkServiceImplBuilder(persister=" + this.persister + ", authentee=" + this.authentee + ")";
      }
   }
}
