package com.airlock.iam.factor.infrastructure.service.airlock2fa;

import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FAAccountId;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.infrastructure.restclient.RestClient;
import com.airlock.iam.common.infrastructure.restclient.RestRequest;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAAdminApiException;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAUserAttributes;
import com.airlock.iam.factor.domain.service.airlock2fa.Airlock2FAAdminApiUserService;
import com.airlock.iam.factor.infrastructure.rest.airlock2fa.futurae.FuturaeAdminApiRequestFactory;
import com.airlock.iam.factor.infrastructure.rest.airlock2fa.futurae.FuturaeAdminApiUserResponseMapper;
import com.airlock.iam.factor.infrastructure.rest.airlock2fa.futurae.FuturaeResponseReader;
import com.airlock.iam.factor.infrastructure.rest.airlock2fa.futurae.transfer.FuturaeAdminUserInformationResponse;
import jakarta.inject.Inject;
import lombok.NonNull;

public final class FuturaeAdminApiUserServiceImpl implements Airlock2FAAdminApiUserService {
   private final RestClient restClient;
   private final FuturaeAdminApiRequestFactory requestFactory;

   @Inject
   public FuturaeAdminApiUserServiceImpl(@NonNull RestClient restClient, @NonNull FuturaeAdminApiRequestFactory requestFactory) {
      if (restClient == null) {
         throw new NullPointerException("restClient is marked non-null but is null");
      }

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

      this.restClient = restClient;
      this.requestFactory = requestFactory;
   }

   @Override
   public Airlock2FAUserAttributes getUserAttributes(Airlock2FAAccountId accountId) throws Airlock2FAAdminApiException {
      return FuturaeAdminApiUserResponseMapper.mapToUserAccount(this.getUserInformation(accountId));
   }

   @Override
   public void lockUserAccount(Airlock2FAAccountId accountId) {
      this.invoke(
         this.requestFactory.lockUnlockUser(accountId.getId(), FuturaeAdminApiRequestFactory.UserStatus.LOCKED_OUT),
         String.format("Could not lock user with id %s", LogUtil.quote(accountId.getId()))
      );
   }

   @Override
   public void unlockUserAccount(Airlock2FAAccountId accountId) {
      this.invoke(
         this.requestFactory.lockUnlockUser(accountId.getId(), FuturaeAdminApiRequestFactory.UserStatus.ENABLED),
         String.format("Could not unlock user with id %s", LogUtil.quote(accountId.getId()))
      );
   }

   private FuturaeAdminUserInformationResponse getUserInformation(Airlock2FAAccountId accountId) {
      RestRequest request = this.requestFactory.getUserInformation(accountId.getId());
      return this.invokeAndMap(
         request,
         String.format("Could not get account information for user with id %s, request: %s", LogUtil.quote(accountId.getId()), request.getDescription()),
         FuturaeAdminUserInformationResponse.class
      );
   }

   private void invoke(RestRequest request, String errorInfo) {
      this.restClient
         .send(request, response -> FuturaeResponseReader.ifErrorResponseThrow(response, request, e -> new Airlock2FAAdminApiException(errorInfo, e)));
   }

   private <T> T invokeAndMap(RestRequest request, String errorInfo, Class<T> toClass) {
      return this.restClient
         .read(
            request, response -> FuturaeResponseReader.readResponse(response, toClass, request).orElseThrow(e -> new Airlock2FAAdminApiException(errorInfo, e))
         );
   }
}
