package com.airlock.iam.authentication.domain.model.processor;

import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.domain.model.mapification.MapificationUtils;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.flow.api.domain.model.session.MultiFlowScopedFlowAttribute;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Map.Entry;
import java.util.stream.Collectors;

public final class LatestAuthenticationAttempt implements MultiFlowScopedFlowAttribute<LatestAuthenticationAttempt> {
   private static final String INFOS_KEY = "infos";
   private static final String USER_ID_KEY = "userId";
   private static final String ATTEMPT_AT_KEY = "attemptAt";
   private static final String SUCCESSFUL_KEY = "successful";
   private final Map<UserId, LatestAuthentication> infos;

   public Optional<LatestAuthentication> getLatestAuthentication(UserId userId) {
      return Optional.ofNullable(this.infos.get(userId));
   }

   public LatestAuthenticationAttempt mergeWith(LatestAuthenticationAttempt newAttribute) {
      Map<UserId, LatestAuthentication> newInfos = new HashMap<>(this.infos);
      newInfos.putAll(newAttribute.infos);
      return new LatestAuthenticationAttempt(newInfos);
   }

   @Override
   public MapifiedValues mapify() {
      return MapifiedValuesBuilder.mapifiedValues()
         .putIfNotEmpty("infos", MapificationUtils.mapifyCollection(this.infos.entrySet(), entry -> mapifyInfo(entry.getKey(), entry.getValue())))
         .build();
   }

   public static LatestAuthenticationAttempt demapify(MapifiedValues values) {
      return new LatestAuthenticationAttempt(
         values.demapifyList("infos", LatestAuthenticationAttempt::demapifyInfo).stream().collect(Collectors.toMap(Entry::getKey, Entry::getValue))
      );
   }

   private static MapifiedValues mapifyInfo(UserId userId, LatestAuthentication latestAuthentication) {
      return MapifiedValuesBuilder.mapifiedValues()
         .put("userId", userId.getValue())
         .putIfPresent("attemptAt", latestAuthentication.getAttemptAt().map(MapificationUtils::mapifyInstant))
         .putIfPresent("successful", latestAuthentication.getSuccessful())
         .build();
   }

   private static Entry<UserId, LatestAuthentication> demapifyInfo(MapifiedValues values) {
      return Map.entry(
         UserId.userId(values.get("userId")),
         new LatestAuthentication(values.demapifyOptional("attemptAt", MapificationUtils::demapifyInstant), values.getOptional("successful"))
      );
   }

   @SuppressFBWarnings(justification = "generated code")
   public LatestAuthenticationAttempt(Map<UserId, LatestAuthentication> infos) {
      this.infos = infos;
   }
}
