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 com.airlock.iam.oauth2.domain.model.token.IdToken;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public final class OpenIDConnectCredentialImpl extends OAuth2CredentialImpl implements OpenIDConnectCredential {
   private static final String ID_TOKEN_KEY = "idToken";
   private final IdToken idToken;

   public OpenIDConnectCredentialImpl(ProvidedUsername username, Map<String, Object> attributes, Set<Role> userRoles, AccessToken accessToken, IdToken idToken) {
      super(username, userRoles, attributes, accessToken);
      this.idToken = idToken;
   }

   @Override
   public OAuth2Credential toNewCredential(ProvidedUsername newUsername) {
      return new OpenIDConnectCredentialImpl(newUsername, this.getAttributeMap(), this.getUserRoles(), this.accessToken(), this.idToken);
   }

   @Override
   public IdToken idToken() {
      return this.idToken;
   }

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

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

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

         if (!super.equals(o)) {
            return false;
         }

         Object this$idToken = this.idToken;
         Object other$idToken = other.idToken;
         return this$idToken == null ? other$idToken == null : this$idToken.equals(other$idToken);
      }
   }

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

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