/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package performa.form;

import java.io.*;
import java.net.*;
import javax.servlet.http.HttpServletRequest;
import oneit.appservices.config.ConfigMgr;
import oneit.components.ParticipantInitialisationContext;
import oneit.logging.*;
import oneit.security.oauth.decorator.OAuthCallbackDecorator;
import oneit.security.oauth.utils.BaseOAuthLoginHandler;
import oneit.servlets.forms.HTTPRequestDetails;
import oneit.utils.*;
import org.json.*;

/**
 *
 * @author Pradip Sabhadiya
 */
public class LinkedInOAuthLoginHandler extends BaseOAuthLoginHandler
{
    protected static final LoggingArea     LOG = LoggingArea.createLoggingArea("BaseOAuthLoginHandler");

    @Override
    public void validateRequest(HttpServletRequest request)
    {
        LogMgr.log(LOG, LogLevel.DEBUG3, "Validation LinkedIn callback request");

        String state = request.getParameter("state");
        String token = (String) request.getSession().getAttribute(OAuthCallbackDecorator.TOKEN_ATTRIB_NAME);
        
        Debug.assertion(CollectionUtils.equals(state, token), "Unothorized access to callback url.");
    }

    @Override
    public OAuthLoginInfo getProfileInfo(HttpServletRequest request) throws Exception
    {
        LogMgr.log(LOG, LogLevel.DEBUG3, "Getting profile data from callback");
        String accessToken = getAccessToken(request);
        
        try
        {
            String profileURL = LinkedInOAuthLoginFP.LINKEDIN_PROFILE_FROM_TOKEN_URL
                                + HTTPRequestDetails.getParamStringURL(CollectionUtils.mapEntry("oauth2_access_token", accessToken).mapEntry("format", "json").toMap());
            
            JSONObject      json        = executeURL(profileURL);
            
            LogMgr.log(LOG, LogLevel.PROCESSING1, "Profile Data found ", json);
            
            OAuthLoginInfo  loginInfo   = new OAuthLoginInfo();
            
            loginInfo.setId(json.getString("id"));
            loginInfo.setFirstName(json.getString("firstName"));
            
            if (json.has("emailAddress"))
            {
                loginInfo.setEmail(json.getString("emailAddress"));
            }
            if (json.has("lastName"))
            {
                loginInfo.setLastName(json.getString("lastName"));
            }
            
            return loginInfo;
        }
        catch (Exception e)
        {
            LogMgr.log(LOG, LogLevel.SYSTEMERROR1, "Exception occured in getProfileInfo");
            throw new NestedException(e, "ERROR while getting user data.");
        }
    }
    
    public String getAccessToken(HttpServletRequest request) throws Exception
    {
        String code = request.getParameter("code");
        String appId = ConfigMgr.getKeyfileString(LinkedInOAuthLoginFP.LINKEDIN_CLIENTID_ATTR_NAME);
        String appSecret = ConfigMgr.getKeyfileString(LinkedInOAuthLoginFP.LINKEDIN_CLIENTSECRET_ATTR_NAME);
        String callbackURL = LinkedInOAuthLoginFP.getCallbackURL(request);
        
        
        @SuppressWarnings("unchecked")
        String accessTokenURL = LinkedInOAuthLoginFP.LINKEDIN_ACCESS_TOKEN_URL
                + HTTPRequestDetails.getParamStringURL(CollectionUtils.mapEntry("grant_type", "authorization_code")
                                                                    .mapEntry("code", code)
                                                                    .mapEntry("redirect_uri", callbackURL)
                                                                    .mapEntry("client_id", appId)
                                                                    .mapEntry("client_secret", appSecret).toMap());
        
        
        JSONObject data = executeURL(accessTokenURL, "POST");

        if(!data.has("access_token"))
        {
            throw new RuntimeException("ERROR: Access Token Invalid: " + data);
        }
        
        return data.getString("access_token");
    }

    public void init(ParticipantInitialisationContext context) throws InitialisationException
    {
        context.setObject(this);
    }
    
    public static JSONObject executeURL(String urlString)
    {
        return executeURL(urlString, null);
    }
    
    public static JSONObject executeURL(String urlString, String method)
    {
        LogMgr.log(LoggingArea.ALL, LogLevel.DEBUG3, "Executing url ", urlString);
        
        try
        {
            URL             url             = new URL(urlString);
            URLConnection   urlConnection   = url.openConnection();
            
            if(StringUtils.subBlanks(method) != null && method.toUpperCase().equals("POST"))
            {
                urlConnection.setDoOutput(true);
            }
             
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            
            return new JSONObject(IOUtils.readerToString(in));
        }
        catch (IOException e)
        {
            LogMgr.log(LoggingArea.ALL, LogLevel.SYSTEMERROR1, "Error in Executing url ", urlString, e);
            throw new NestedException(e);
        }
        catch(JSONException je)
        {
            LogMgr.log(LoggingArea.ALL, LogLevel.SYSTEMERROR1, "Error in Parsing Json Data", urlString, je);
            throw new NestedException(je);
        }
    }
}
