package com.airlock.iam.oauth2.domain.model.client;

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.api.domain.model.user.ProvidedUsername;
import com.airlock.iam.common.domain.service.mapification.Mapifier;
import com.airlock.iam.login.api.domain.model.oauth2.AccessToken;
import com.airlock.iam.login.api.domain.model.oauth2.OAuth2Credential;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class OAuth2CredentialImpl implements OAuth2Credential {
   protected static final String USERNAME_KEY = "username";
   protected static final String ROLES_KEY = "roles";
   protected static final String ATTRIBUTES_KEY = "attributes";
   protected static final String ACCESS_TOKEN_KEY = "token";
   private final ProvidedUsername username;
   private final Set<Role> userRoles;
   private final Map<String, Object> attributes;
   private final AccessToken accessToken;

   @Override
   public OAuth2Credential toNewCredential(ProvidedUsername newUsername) {
      return new OAuth2CredentialImpl(newUsername, this.userRoles, this.attributes, this.accessToken);
   }

   @Override
   public Set<String> getAttributeKeys() {
      return this.attributes.keySet();
   }

   @Override
   public Object getAttributeValue(String key) {
      return this.attributes.get(key);
   }

   @Override
   public AccessToken accessToken() {
      return this.accessToken;
   }

   protected Map<String, Object> getAttributeMap() {
      return this.attributes;
   }

   @Override
   public MapifiedValues mapify() {
      return MapifiedValuesBuilder.mapifiedValues()
         .put("username", this.username.getValue())
         .putIfNotEmpty("roles", this.userRoles.stream().map(Role::formatRole).collect(Collectors.toSet()))
         .put("attributes", MapificationUtils.mapifyMap(this.attributes, Mapifier.getInstance()::mapify))
         .put("token", this.accessToken.value())
         .build();
   }

   public static OAuth2CredentialImpl demapify(MapifiedValues values) {
      return new OAuth2CredentialImpl(
         values.simpleValue("username", ProvidedUsername::providedUsernameOf),
         values.<String>getSet("roles").stream().map(Role::parseRole).collect(Collectors.toSet()),
         values.demapifyValue("attributes", v -> MapificationUtils.demapifyMap(v.asMap(), Mapifier.getInstance()::demapify)),
         values.simpleValue("token", AccessToken::new)
      );
   }

   @SuppressFBWarnings(justification = "generated code")
   public OAuth2CredentialImpl(ProvidedUsername username, Set<Role> userRoles, Map<String, Object> attributes, AccessToken accessToken) {
      this.username = username;
      this.userRoles = userRoles;
      this.attributes = attributes;
      this.accessToken = accessToken;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public boolean equals(Object o) {
      if (o == this) {
         return true;
      } else if (!(o instanceof OAuth2CredentialImpl other)) {
         return false;
      } else {
         if (!other.canEqual(this)) {
            return false;
         }

         Object this$username = this.username;
         Object other$username = other.username;
         if (this$username == null ? other$username == null : this$username.equals(other$username)) {
            Object this$userRoles = this.userRoles;
            Object other$userRoles = other.userRoles;
            if (this$userRoles == null ? other$userRoles == null : this$userRoles.equals(other$userRoles)) {
               Object this$attributes = this.attributes;
               Object other$attributes = other.attributes;
               if (this$attributes == null ? other$attributes == null : this$attributes.equals(other$attributes)) {
                  Object this$accessToken = this.accessToken;
                  Object other$accessToken = other.accessToken;
                  return this$accessToken == null ? other$accessToken == null : this$accessToken.equals(other$accessToken);
               } else {
                  return false;
               }
            } else {
               return false;
            }
         } else {
            return false;
         }
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   protected boolean canEqual(Object other) {
      return other instanceof OAuth2CredentialImpl;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public ProvidedUsername getUsername() {
      return this.username;
   }

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