/*
 * Copyright 2017 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
 *
 *       http://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 leap.oauth2.webapp.token;

import leap.core.annotation.Inject;
import leap.lang.Strings;
import leap.lang.codec.Base64;
import leap.lang.http.ContentTypes;
import leap.lang.http.HTTP;
import leap.lang.http.Headers;
import leap.lang.http.client.HttpClient;
import leap.lang.http.client.HttpRequest;
import leap.lang.http.client.HttpResponse;
import leap.lang.json.JSON;
import leap.lang.json.JsonValue;
import leap.lang.logging.Log;
import leap.lang.logging.LogFactory;
import leap.oauth2.webapp.OAuth2InternalServerException;
import leap.oauth2.webapp.OAuth2Config;
import leap.oauth2.webapp.OAuth2ResponseException;
import leap.oauth2.webapp.Oauth2InvalidTokenException;

import java.util.Map;
import java.util.Objects;

public class DefaultTokenInfoLookup implements TokenInfoLookup {

    private static final Log log = LogFactory.get(DefaultTokenInfoLookup.class);

    protected @Inject OAuth2Config config;
    protected @Inject HttpClient   httpClient;

    @Override
    public TokenInfo lookupByAccessToken(String at) {
        if(null == config.getTokenInfoUrl()) {
            throw new IllegalStateException("The tokenInfoUrl must be configured");
        }

        HttpRequest request = httpClient.request(config.getTokenInfoUrl())
                .addQueryParam("access_token", at)
                .setMethod(HTTP.Method.GET);

        if(null != config.getClientId()){
            request.addHeader(Headers.AUTHORIZATION, "Basic " +
                    Base64.encode(config.getClientId()+":"+config.getClientSecret()));
        }

        HttpResponse response = request.send();
        
        if(ContentTypes.APPLICATION_JSON_TYPE.isCompatible(response.getContentType())){
            String content = response.getString();

            log.debug("Received response : {}", content);

            JsonValue json = JSON.parse(content);

            if(!json.isMap()) {
                throw new OAuth2InternalServerException("Invalid response from auth server : not a json map");
            }else{
                Map<String, Object> map = json.asMap();
                String error = (String)map.get("error");
                String desc  = Objects.toString(map.get("error_description"));

                if(Strings.isEmpty(desc)) {
                    log.error("Err get token info from '{}' : {} - {}", config.getTokenUrl(), response.getStatus(), content);
                }else {
                    log.error("Err get token info from '{}' : {} - {}", config.getTokenUrl(), response.getStatus(), desc);
                }

                if(Strings.isEmpty(error)) {
                    return createTokenInfo(map);
                }else{
                    if(response.getStatus() == HTTP.SC_UNAUTHORIZED){
                        throw new Oauth2InvalidTokenException(response.getStatus(),error, desc);
                    }else if(!response.is2xx()){
                        throw new OAuth2ResponseException(response.getStatus(),error, desc);
                    }else {
                        throw new OAuth2InternalServerException("Auth server response error '" + error + "' : " + desc);
                    }
                }
            }
        }else{
            throw new OAuth2InternalServerException("Invalid response from auth server");
        }
    }

    protected TokenInfo createTokenInfo(Map<String, Object> map) {
        SimpleTokenInfo info = new SimpleTokenInfo();

        info.setClientId((String)map.get("client_id"));
        info.setUserId((String)map.get("user_id"));
        info.setCreated(System.currentTimeMillis());
        info.setExpiresIn(((Integer)map.get("expires_in")));
        info.setScope((String)map.get("scope"));
        info.setClaims(map);

        return info;
    }
}

@@ -1093,16 +1093,49 @@
 actory;%0A
+import leap.lang.time.StopWatch;%0A
 import l
@@ -2196,32 +2196,77 @@
 )));%0A        %7D%0A%0A
+        StopWatch sw = StopWatch.startNew();%0A
         HttpResp
@@ -2827,24 +2827,254 @@
 t(%22error%22);%0A
+                if(Strings.isEmpty(error)) %7B%0A                    log.info(%22Request token info from oauth2 server use %7B%7Dms%22, sw.getElapsedMilliseconds());%0A                    return createTokenInfo(map);%0A                %7Delse%7B%0A    
             
@@ -3140,17 +3140,17 @@
 ion%22));%0A
-%0A
+ 
         
@@ -3149,32 +3149,35 @@
                 
+   
 if(Strings.isEmp
@@ -3200,32 +3200,36 @@
                 
+   
+ 
 log.error(%22Err g
@@ -3330,32 +3330,36 @@
             
+    
 %7Delse %7B%0A
             
@@ -3338,32 +3338,36 @@
         %7Delse %7B%0A
+    
                 
@@ -3493,127 +3493,13 @@
     
-%7D%0A%0A                if(Strings.isEmpty(error)) %7B%0A                    return createTokenInfo(map);%0A                %7Delse%7B
+    %7D
 %0A   

add logging for request token info success