/*
 * Copyright 2006-2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.github.microservice.auth.server.core.oauth2.serialize;


import java.io.IOException;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import com.github.microservice.auth.server.core.oauth2.token.DefaultOAuth2AccessToken;
import com.github.microservice.auth.server.core.oauth2.token.DefaultOAuth2RefreshToken;
import com.github.microservice.auth.server.core.oauth2.token.OAuth2AccessToken;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.github.microservice.auth.server.core.oauth2.util.OAuth2Utils;

/**
 * <p>
 * Provides the ability to deserialize JSON response into an {@link org.springframework.security.oauth2.common.OAuth2AccessToken} with jackson2 by implementing
 * {@link com.fasterxml.jackson.databind.JsonDeserializer}.
 * </p>
 * <p>
 * The expected format of the access token is defined by <a
 * href="https://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-5.1">Successful Response</a>.
 * </p>
 *
 * <p>
 * @deprecated See the <a href="https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Migration-Guide">OAuth 2.0 Migration Guide</a> for Spring Security 5.
 *
 * @author Rob Winch
 * @author Brian Clozel
 * @see org.springframework.security.oauth2.common.OAuth2AccessTokenJackson2Serializer
 */
@SuppressWarnings("serial")
@Deprecated
public final class OAuth2AccessTokenJackson2Deserializer extends StdDeserializer<OAuth2AccessToken> {

	public OAuth2AccessTokenJackson2Deserializer() {
		super(OAuth2AccessToken.class);
	}

	@Override
	public OAuth2AccessToken deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
			JsonProcessingException {

		String tokenValue = null;
		String tokenType = null;
		String refreshToken = null;
		Long expiresIn = null;
		Set<String> scope = null;
		Map<String, Object> additionalInformation = new LinkedHashMap<String, Object>();

		// TODO What should occur if a parameter exists twice
		while (jp.nextToken() != JsonToken.END_OBJECT) {
			String name = jp.getCurrentName();
			jp.nextToken();
			if (OAuth2AccessToken.ACCESS_TOKEN.equals(name)) {
				tokenValue = jp.getText();
			}
			else if (OAuth2AccessToken.TOKEN_TYPE.equals(name)) {
				tokenType = jp.getText();
			}
			else if (OAuth2AccessToken.REFRESH_TOKEN.equals(name)) {
				refreshToken = jp.getText();
			}
			else if (OAuth2AccessToken.EXPIRES_IN.equals(name)) {
				try {
					expiresIn = jp.getLongValue();
				} catch (JsonParseException e) {
					expiresIn = Long.valueOf(jp.getText());
				}
			}
			else if (OAuth2AccessToken.SCOPE.equals(name)) {
				scope = parseScope(jp);
			} else {
				additionalInformation.put(name, jp.readValueAs(Object.class));
			}
		}

		// TODO What should occur if a required parameter (tokenValue or tokenType) is missing?

		DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(tokenValue);
		accessToken.setTokenType(tokenType);
		if (expiresIn != null && expiresIn != 0) {
			accessToken.setExpiration(new Date(System.currentTimeMillis() + (expiresIn * 1000)));
		}
		if (refreshToken != null) {
			accessToken.setRefreshToken(new DefaultOAuth2RefreshToken(refreshToken));
		}
		accessToken.setScope(scope);
		accessToken.setAdditionalInformation(additionalInformation);

		return accessToken;
	}

	private Set<String> parseScope(JsonParser jp) throws JsonParseException, IOException {
		Set<String> scope;
		if (jp.getCurrentToken() == JsonToken.START_ARRAY) {
			scope = new TreeSet<String>();
			while (jp.nextToken() != JsonToken.END_ARRAY) {
				scope.add(jp.getValueAsString());
			}
		} else {
			String text = jp.getText();
			scope = OAuth2Utils.parseParameterList(text);
		}
		return scope;
	}
}