/*
   * Find the Subject identifier among federated claims
   */
  public static String getFederatedSubjectFromClaims(
      AuthenticationContext context, String otherDialect) throws FrameworkException {
    String value;
    boolean useLocalClaimDialect = context.getExternalIdP().useDefaultLocalIdpDialect();
    String userIdClaimURI = context.getExternalIdP().getUserIdClaimUri();
    Map<ClaimMapping, String> claimMappings = context.getSubject().getUserAttributes();

    if (useLocalClaimDialect) {
      Map<String, String> extAttributesValueMap =
          FrameworkUtils.getClaimMappings(claimMappings, false);
      Map<String, String> mappedAttrs = null;
      try {
        mappedAttrs =
            ClaimMetadataHandler.getInstance()
                .getMappingsMapFromOtherDialectToCarbon(
                    otherDialect, extAttributesValueMap.keySet(), context.getTenantDomain(), true);
      } catch (ClaimMetadataException e) {
        throw new FrameworkException("Error while loading claim mappings.", e);
      }

      String spUserIdClaimURI = mappedAttrs.get(userIdClaimURI);
      value = extAttributesValueMap.get(spUserIdClaimURI);
    } else {
      ClaimMapping claimMapping = new ClaimMapping();
      Claim claim = new Claim();
      claim.setClaimUri(userIdClaimURI);
      claimMapping.setRemoteClaim(claim);
      value = claimMappings.get(claimMapping);
    }
    return value;
  }
  /*
   * Find the Subject identifier among federated claims
   */
  public static String getFederatedSubjectFromClaims(
      IdentityProvider identityProvider, Map<ClaimMapping, String> claimMappings) {

    String userIdClaimURI = identityProvider.getClaimConfig().getUserClaimURI();
    ClaimMapping claimMapping = new ClaimMapping();
    Claim claim = new Claim();
    claim.setClaimUri(userIdClaimURI);
    claimMapping.setRemoteClaim(claim);
    claimMapping.setLocalClaim(claim);
    return claimMappings.get(claimMapping);
  }
  /**
   * @param claimMappings
   * @param useLocalDialectAsKey
   * @return
   */
  public static Map<String, String> getClaimMappings(
      Map<ClaimMapping, String> claimMappings, boolean useLocalDialectAsKey) {

    Map<String, String> remoteToLocalClaimMap = new HashMap<String, String>();

    for (Entry<ClaimMapping, String> entry : claimMappings.entrySet()) {
      ClaimMapping claimMapping = entry.getKey();
      if (useLocalDialectAsKey) {
        remoteToLocalClaimMap.put(claimMapping.getLocalClaim().getClaimUri(), entry.getValue());
      } else {
        remoteToLocalClaimMap.put(claimMapping.getRemoteClaim().getClaimUri(), entry.getValue());
      }
    }
    return remoteToLocalClaimMap;
  }
  /**
   * Get Subject Attributes
   *
   * @param token
   * @return
   */
  @Override
  protected Map<ClaimMapping, String> getSubjectAttributes(OAuthClientResponse token) {

    Map<ClaimMapping, String> claims = new HashMap<ClaimMapping, String>();

    try {

      String json =
          sendRequest(
              token.getParam(GoogleOAuth2AuthenticationConstant.GOOGLE_USERINFO_ENDPOINT),
              token.getParam(OIDCAuthenticatorConstants.ACCESS_TOKEN));
      if (StringUtils.isNotBlank(json)) {
        Map<String, Object> jsonObject = JSONUtils.parseJSON(json);

        if (jsonObject != null) {
          for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            claims.put(
                ClaimMapping.build(entry.getKey(), entry.getKey(), null, false),
                entry.getValue().toString());
            if (log.isDebugEnabled()) {
              log.debug(
                  "Adding claim from end-point data mapping : "
                      + entry.getKey()
                      + " - "
                      + entry.getValue());
            }
          }
        }
      }
    } catch (Exception e) {
      log.error("Error occurred while accessing google user info endpoint", e);
    }

    return claims;
  }
  protected void buildClaimMappings(
      Map<ClaimMapping, String> claims, Map.Entry<String, Object> entry, String separator) {
    String claimValue = null;
    if (StringUtils.isBlank(separator)) {
      separator = IdentityCoreConstants.MULTI_ATTRIBUTE_SEPARATOR_DEFAULT;
    }
    try {
      JSONArray jsonArray = (JSONArray) JSONValue.parseWithException(entry.getValue().toString());
      if (jsonArray != null && jsonArray.size() > 0) {
        Iterator attributeIterator = jsonArray.iterator();
        while (attributeIterator.hasNext()) {
          if (claimValue == null) {
            claimValue = attributeIterator.next().toString();
          } else {
            claimValue = claimValue + separator + attributeIterator.next().toString();
          }
        }
      }
    } catch (Exception e) {
      claimValue = entry.getValue().toString();
    }

    claims.put(ClaimMapping.build(entry.getKey(), entry.getKey(), null, false), claimValue);
    if (log.isDebugEnabled()
        && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.USER_CLAIMS)) {
      log.debug(
          "Adding claim mapping : "
              + entry.getKey()
              + " <> "
              + entry.getKey()
              + " : "
              + claimValue);
    }
  }
  /**
   * @param claimMappings
   * @return
   */
  public static Map<String, String> getClaimMappings(
      ClaimMapping[] claimMappings, boolean useLocalDialectAsKey) {

    Map<String, String> remoteToLocalClaimMap = new HashMap<String, String>();

    for (ClaimMapping claimMapping : claimMappings) {
      if (useLocalDialectAsKey) {
        remoteToLocalClaimMap.put(
            claimMapping.getLocalClaim().getClaimUri(),
            claimMapping.getRemoteClaim().getClaimUri());
      } else {
        remoteToLocalClaimMap.put(
            claimMapping.getRemoteClaim().getClaimUri(),
            claimMapping.getLocalClaim().getClaimUri());
      }
    }
    return remoteToLocalClaimMap;
  }
  /**
   * @param attributeValue
   * @return
   */
  public static Map<ClaimMapping, String> buildClaimMappings(Map<String, String> attributeValue) {

    Map<ClaimMapping, String> claimMap = new HashMap<ClaimMapping, String>();

    for (Iterator<Entry<String, String>> iterator = attributeValue.entrySet().iterator();
        iterator.hasNext(); ) {
      Entry<String, String> entry = iterator.next();
      if (entry.getValue() == null) {
        continue;
      }
      claimMap.put(
          ClaimMapping.build(entry.getKey(), entry.getKey(), null, false), entry.getValue());
    }

    return claimMap;
  }
  /**
   * Get subject attributes.
   *
   * @param token OAuthClientResponse
   * @param authenticatorProperties Map<String, String>
   * @return Map<ClaimMapping, String> Claim mappings.
   */
  protected Map<ClaimMapping, String> getSubjectAttributes(
      OAuthClientResponse token, Map<String, String> authenticatorProperties) {

    Map<ClaimMapping, String> claims = new HashMap<>();

    try {

      String accessToken = token.getParam(OIDCAuthenticatorConstants.ACCESS_TOKEN);
      String url = getUserInfoEndpoint(token, authenticatorProperties);

      String json = sendRequest(url, accessToken);

      if (StringUtils.isBlank(json)) {
        if (log.isDebugEnabled()) {
          log.debug("Unable to fetch user claims. Proceeding without user claims");
        }
        return claims;
      }

      Map<String, Object> jsonObject = JSONUtils.parseJSON(json);

      for (Map.Entry<String, Object> data : jsonObject.entrySet()) {

        String key = data.getKey();

        claims.put(ClaimMapping.build(key, key, null, false), jsonObject.get(key).toString());

        if (log.isDebugEnabled()
            && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.USER_CLAIMS)) {
          log.debug(
              "Adding claims from end-point data mapping : "
                  + key
                  + " - "
                  + jsonObject.get(key).toString());
        }
      }

    } catch (Exception e) {
      log.error("Error occurred while accessing user info endpoint", e);
    }

    return claims;
  }
  /*
   * Process the response and returns the results
   */
  private Map<ClaimMapping, String> getAssertionStatements(Assertion assertion) {

    Map<ClaimMapping, String> results = new HashMap<ClaimMapping, String>();

    if (assertion != null) {

      List<AttributeStatement> attributeStatementList = assertion.getAttributeStatements();

      if (attributeStatementList != null) {
        for (AttributeStatement statement : attributeStatementList) {
          List<Attribute> attributesList = statement.getAttributes();
          for (Attribute attribute : attributesList) {
            Element value = attribute.getAttributeValues().get(0).getDOM();
            String attributeValue = value.getTextContent();
            results.put(
                ClaimMapping.build(attribute.getName(), attribute.getName(), null, false),
                attributeValue);
          }
        }
      }
    }
    return results;
  }