package com.airlock.iam.admin.domain.model.contextdata;

import com.airlock.iam.common.api.domain.model.contextdata.ContextData;
import com.airlock.iam.common.api.domain.model.contextdata.ContextDataItem;
import com.airlock.iam.common.domain.model.contextdata.ContextDataAccessController;
import com.airlock.iam.common.domain.model.contextdata.ContextDataFactory;
import com.airlock.iam.common.domain.model.contextdata.ContextDataImpl;
import com.airlock.iam.common.domain.model.contextdata.ContextDataItemImpl;
import com.airlock.iam.common.domain.model.user.profileitems.UserProfileItemDetails;
import com.airlock.iam.common.domain.model.user.profileitems.UserProfileItems;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Map.Entry;

public class AdminContextDataFactory implements ContextDataFactory {
   private final UserProfileItems profileItems;
   private final ContextDataAccessController accessController;

   public ContextData fromPersistency(Map<String, Object> persistentContextData) {
      return this.asContextData(persistentContextData, true);
   }

   public ContextData fromInput(Map<String, Object> inputContextData) {
      return this.asContextData(inputContextData, false);
   }

   private ContextData asContextData(Map<String, Object> dataMap, boolean fromPersistency) {
      Set<ContextDataItem<?>> items = new HashSet<>();

      for (Entry<String, Object> entry : dataMap.entrySet()) {
         Optional<UserProfileItemDetails> profileItem = this.profileItems.forContextData(entry.getKey());
         items.add(this.createContextDataItem(fromPersistency, entry, profileItem, dataMap));
      }

      return new ContextDataImpl(items);
   }

   private ContextDataItemImpl<?> createContextDataItem(
      boolean fromPersistency, Entry<String, Object> entry, Optional<UserProfileItemDetails> profileItem, Map<String, Object> contextData
   ) {
      if (profileItem.isPresent()) {
         Object value;
         if (fromPersistency) {
            value = profileItem.get().transformFromPersistency(entry.getValue());
         } else {
            Object transformedValue = profileItem.get().transformToPersistency(entry.getValue());
            value = transformedValue == null ? entry.getValue() : transformedValue;
         }

         return new ContextDataItemImpl(
            entry.getKey(),
            value,
            this.accessController.isVisible(entry.getKey()),
            this.isEditable(entry, profileItem, contextData),
            Optional.of((newValue, originalValue) -> profileItem.get().validate(newValue, originalValue.orElse(null), null))
         );
      } else {
         return new ContextDataItemImpl(
            entry.getKey(),
            entry.getValue(),
            this.accessController.isVisible(entry.getKey()),
            this.isEditable(entry, profileItem, contextData),
            Optional.empty()
         );
      }
   }

   private boolean isEditable(Entry<String, Object> entry, Optional<UserProfileItemDetails> profileItem, Map<String, Object> contextData) {
      return !this.accessController.isEditable(entry.getKey()) ? false : profileItem.<Boolean>map(item -> item.isModifiable(contextData)).orElse(true);
   }

   @SuppressFBWarnings(justification = "generated code")
   public AdminContextDataFactory(UserProfileItems profileItems, ContextDataAccessController accessController) {
      this.profileItems = profileItems;
      this.accessController = accessController;
   }
}
