package com.airlock.iam.admin.domain.model.token.generic;

import com.airlock.iam.admin.api.domain.model.token.generic.GenericToken;
import com.airlock.iam.core.misc.impl.persistency.token.TokenBeanImpl;
import com.airlock.iam.core.misc.persistency.token.TokenAssignment;
import com.airlock.iam.core.misc.persistency.token.TokenBean;
import com.airlock.iam.core.misc.util.DateRange;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

public final class TokenBasedGenericTokenConverter {
   public static final String SERIAL_ID = "serialId";
   public static final String ACTIVE = "active";
   public static final String ACTIVATION_DATE = "activationDate";
   public static final String VALID_FROM = "validFrom";
   public static final String VALID_TO = "validTo";
   public static final String GENERATION_DATE = "generationDate";
   public static final String FIRST_USAGE_DATE = "firstUsageDate";
   public static final String LATEST_USAGE_DATE = "latestUsageDate";
   public static final String TOTAL_USAGES = "totalUsages";
   public static final String TRACKING_ID = "trackingId";
   public static final String TOKEN_DATA = "tokenData";
   public static final String OTHER_ASSIGNEES = "otherAssignees";
   public static final String GENERIC_DATA_ELEMENT_1 = "genericDataElement1";
   public static final String GENERIC_DATA_ELEMENT_2 = "genericDataElement2";
   public static final String GENERIC_DATA_ELEMENT_3 = "genericDataElement3";
   public static final String GENERIC_DATA_ELEMENT_4 = "genericDataElement4";
   public static final String GENERIC_DATA_ELEMENT_5 = "genericDataElement5";
   public static final String GENERIC_DATA_ELEMENT_6 = "genericDataElement6";
   public static final String GENERIC_DATA_ELEMENT_7 = "genericDataElement7";
   public static final String GENERIC_DATA_ELEMENT_8 = "genericDataElement8";
   public static final String GENERIC_DATA_ELEMENT_9 = "genericDataElement9";
   public static final String GENERIC_DATA_ELEMENT_10 = "genericDataElement10";
   public static final String GENERIC_DATA_ELEMENT_11 = "genericDataElement11";
   public static final String GENERIC_DATA_ELEMENT_12 = "genericDataElement12";
   private final String tokenType;
   private final Optional<String> username;

   private TokenBasedGenericTokenConverter(String tokenType, Optional<String> username) {
      this.tokenType = tokenType;
      this.username = username;
   }

   public static TokenBasedGenericTokenConverter getInstance(String tokenType, Optional<String> username) {
      return new TokenBasedGenericTokenConverter(tokenType, username);
   }

   public GenericToken toGenericToken(TokenBean bean, List<TokenBean> allUserTokens) {
      Map<String, Object> attributes = new HashMap<>();
      attributes.put("serialId", bean.getSerialId());
      attributes.put("active", bean.isActive());
      attributes.put("activationDate", bean.getActivationDate());
      attributes.put("validFrom", bean.getValidityRange() != null ? bean.getValidityRange().getFromDate() : null);
      attributes.put("validTo", bean.getValidityRange() != null ? bean.getValidityRange().getToDate() : null);
      attributes.put("generationDate", bean.getGenerationDate());
      attributes.put("firstUsageDate", bean.getFirstUsageDate());
      attributes.put("latestUsageDate", bean.getLatestUsageDate());
      attributes.put("totalUsages", bean.getTotalUsages());
      attributes.put("trackingId", bean.getTrackingId());
      attributes.put("otherAssignees", this.otherAssignees(bean));
      attributes.put("tokenData", bean.getTokenData());
      attributes.put("genericDataElement1", bean.getGenericDataElement1());
      attributes.put("genericDataElement2", bean.getGenericDataElement2());
      attributes.put("genericDataElement3", bean.getGenericDataElement3());
      attributes.put("genericDataElement4", bean.getGenericDataElement4());
      attributes.put("genericDataElement5", bean.getGenericDataElement5());
      attributes.put("genericDataElement6", bean.getGenericDataElement6());
      attributes.put("genericDataElement7", bean.getGenericDataElement7());
      attributes.put("genericDataElement8", bean.getGenericDataElement8());
      attributes.put("genericDataElement9", bean.getGenericDataElement9());
      attributes.put("genericDataElement10", bean.getGenericDataElement10());
      attributes.put("genericDataElement11", bean.getGenericDataElement11());
      attributes.put("genericDataElement12", bean.getGenericDataElement12());
      return GenericToken.builder()
         .currentUserName(this.username)
         .tokenId(bean.getTokenId())
         .mappedAttributes(new HashMap<>(attributes))
         .previousTokenId(this.getPreviousTokenId(bean, allUserTokens))
         .nextTokenIds(this.getNextTokenIds(bean, allUserTokens))
         .tokenDataKey("tokenData")
         .build();
   }

   private String[] otherAssignees(TokenBean bean) {
      return bean.getAssignments()
         .stream()
         .<String>map(TokenAssignment::getUser)
         .filter(uid -> this.username.isEmpty() || !uid.equals(this.username.get()))
         .toArray(String[]::new);
   }

   private List<String> getNextTokenIds(TokenBean bean, List<TokenBean> allUserTokens) {
      return !this.username.isEmpty() && bean.getTokenId() != null
         ? allUserTokens.stream()
            .filter(token -> bean.getTokenId().equals(token.getObsoletesTokenId()))
            .<String>map(TokenBean::getTokenId)
            .collect(Collectors.toList())
         : Collections.emptyList();
   }

   private Optional<String> getPreviousTokenId(TokenBean bean, List<TokenBean> allUserTokens) {
      Optional<String> previousTokenId = Optional.empty();
      if (this.username.isPresent() && bean.getObsoletesTokenId() != null) {
         Optional<TokenBean> previousToken = allUserTokens.stream().filter(token -> bean.getObsoletesTokenId().equals(token.getTokenId())).findAny();
         if (previousToken.isPresent()) {
            previousTokenId = Optional.of(previousToken.get().getTokenId());
         }
      }

      return previousTokenId;
   }

   public TokenBean toTokenBean(GenericToken token) {
      TokenBean bean = TokenBeanImpl.newToken(this.tokenType, token.getTokenId());
      this.toTokenBean(token, bean);
      return bean;
   }

   public void toTokenBean(GenericToken token, TokenBean bean) {
      bean.setSerialId(token.getAttributeOrDefault("serialId", bean.getSerialId()));
      bean.setActive(token.getAttributeOrDefault("active", bean.isActive()));
      bean.setActivationDate(token.getAttributeOrDefault("activationDate", bean.getActivationDate()));
      DateRange validityRange = DateRange.of(
         token.getAttributeOrDefault("validFrom", bean.getValidityRange() != null ? bean.getValidityRange().getFromDate() : null),
         token.getAttributeOrDefault("validTo", bean.getValidityRange() != null ? bean.getValidityRange().getToDate() : null)
      );
      bean.setValidityRange(validityRange);
      bean.setGenerationDate(token.getAttributeOrDefault("generationDate", bean.getGenerationDate()));
      bean.setFirstUsageDate(token.getAttributeOrDefault("firstUsageDate", bean.getFirstUsageDate()));
      bean.setLatestUsageDate(token.getAttributeOrDefault("latestUsageDate", bean.getLatestUsageDate()));
      bean.setTotalUsages(token.getAttributeOrDefault("totalUsages", bean.getTotalUsages()));
      bean.setTrackingId(token.getAttributeOrDefault("trackingId", bean.getTrackingId()));
      bean.setTokenData(token.getAttributeOrDefault("tokenData", bean.getTokenData()));
      bean.setGenericDataElement1(token.getAttributeOrDefault("genericDataElement1", bean.getGenericDataElement1()));
      bean.setGenericDataElement2(token.getAttributeOrDefault("genericDataElement2", bean.getGenericDataElement2()));
      bean.setGenericDataElement3(token.getAttributeOrDefault("genericDataElement3", bean.getGenericDataElement3()));
      bean.setGenericDataElement4(token.getAttributeOrDefault("genericDataElement4", bean.getGenericDataElement4()));
      bean.setGenericDataElement5(token.getAttributeOrDefault("genericDataElement5", bean.getGenericDataElement5()));
      bean.setGenericDataElement6(token.getAttributeOrDefault("genericDataElement6", bean.getGenericDataElement6()));
      bean.setGenericDataElement7(token.getAttributeOrDefault("genericDataElement7", bean.getGenericDataElement7()));
      bean.setGenericDataElement8(token.getAttributeOrDefault("genericDataElement8", bean.getGenericDataElement8()));
      bean.setGenericDataElement9(token.getAttributeOrDefault("genericDataElement9", bean.getGenericDataElement9()));
      bean.setGenericDataElement10(token.getAttributeOrDefault("genericDataElement10", bean.getGenericDataElement10()));
      bean.setGenericDataElement11(token.getAttributeOrDefault("genericDataElement11", bean.getGenericDataElement11()));
      bean.setGenericDataElement12(token.getAttributeOrDefault("genericDataElement12", bean.getGenericDataElement12()));
   }
}
