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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.base.domain.model.mapification.MapificationUtils;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.common.api.domain.model.role.Role;
import com.airlock.iam.common.domain.service.mapification.Mapifier;
import com.airlock.iam.common.domain.service.sso.ContextDataEntry;
import com.airlock.iam.flow.shared.api.domain.model.session.UserSessionScopedFlowAttribute;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.NonNull;

public final class SsoFlowSessionAttribute implements UserSessionScopedFlowAttribute<SsoFlowSessionAttribute> {
   private final Set<Role> roles;
   private final Set<ContextDataEntry> contextData;

   public SsoFlowSessionAttribute mergeWith(SsoFlowSessionAttribute newAttribute) {
      throw new FailureException("Cannot replace SSO flow session attribute");
   }

   public SsoFlowSessionAttribute copyAttribute() {
      return new SsoFlowSessionAttribute(this.roles, this.contextData);
   }

   @Override
   public MapifiedValues mapify() {
      Mapifier mapifier = Mapifier.getInstance();
      return MapifiedValuesBuilder.mapifiedValues()
         .putIfNotEmpty("roles", this.roles.stream().map(Role::formatRole).toList())
         .put(
            "contextData",
            MapificationUtils.mapifyMap(this.contextData.stream().collect(Collectors.toMap(ContextDataEntry::key, ContextDataEntry::value)), mapifier::mapify)
         )
         .build();
   }

   public static SsoFlowSessionAttribute demapify(MapifiedValues values) {
      Mapifier mapifier = Mapifier.getInstance();
      return new SsoFlowSessionAttribute(
         values.demapifySimpleSet("roles", r -> Role.parseRole((String)r)),
         MapificationUtils.demapifyMap(values.get("contextData"), mapifier::demapify)
            .entrySet()
            .stream()
            .map(e -> new ContextDataEntry(e.getKey(), e.getValue()))
            .collect(Collectors.toSet())
      );
   }

   public static SsoFlowSessionAttribute deserializeSessionAttribute(@NonNull Map<String, String> rawValues) {
      if (rawValues == null) {
         throw new NullPointerException("rawValues is marked non-null but is null");
      }

      Set<Role> parsedRoles = SsoFlowSessionAttributesSeralizer.deserializeRoles(rawValues);
      Set<ContextDataEntry> parsedContextData = SsoFlowSessionAttributesSeralizer.deserializeContextData(rawValues);
      return new SsoFlowSessionAttribute(parsedRoles, parsedContextData);
   }

   @SuppressFBWarnings(justification = "generated code")
   public Set<Role> getRoles() {
      return this.roles;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Set<ContextDataEntry> getContextData() {
      return this.contextData;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public boolean equals(Object o) {
      if (o == this) {
         return true;
      } else if (!(o instanceof SsoFlowSessionAttribute other)) {
         return false;
      } else {
         Object this$roles = this.roles;
         Object other$roles = other.roles;
         if (this$roles == null ? other$roles == null : this$roles.equals(other$roles)) {
            Object this$contextData = this.contextData;
            Object other$contextData = other.contextData;
            return this$contextData == null ? other$contextData == null : this$contextData.equals(other$contextData);
         } else {
            return false;
         }
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public int hashCode() {
      int PRIME = 59;
      int result = 1;
      Object $roles = this.roles;
      result = result * 59 + ($roles == null ? 43 : $roles.hashCode());
      Object $contextData = this.contextData;
      return result * 59 + ($contextData == null ? 43 : $contextData.hashCode());
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public String toString() {
      return "SsoFlowSessionAttribute(roles=" + this.getRoles() + ", contextData=" + this.getContextData() + ")";
   }

   @SuppressFBWarnings(justification = "generated code")
   public SsoFlowSessionAttribute(Set<Role> roles, Set<ContextDataEntry> contextData) {
      this.roles = roles;
      this.contextData = contextData;
   }
}
