package performa.utils;


import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
import oneit.components.*;
import oneit.logging.LogLevel;
import oneit.logging.LogMgr;
import oneit.logging.LoggingArea;
import oneit.objstore.ObjectStatus;
import oneit.objstore.ObjectTransaction;
import oneit.objstore.services.TransactionServicesFactory;
import oneit.security.Role;
import oneit.security.SecUser;
import oneit.security.oauth.decorator.OAuthCallbackDecorator;
import oneit.security.oauth.utils.BaseOAuthLoginHandler;
import oneit.servlets.security.SessionSecUserDecorator;
import oneit.servlets.utils.BaseHttpServletRequest;
import oneit.servlets.utils.BaseHttpServletResponse;
import oneit.servlets.utils.decorator.ServletDecorator;
import oneit.servlets.utils.decorator.ServletDecoratorConfig;
import oneit.utils.*;
import oneit.utils.parsers.FieldException;
import performa.orm.Candidate;
import performa.orm.CompanyUser;
import performa.orm.Company;
import performa.orm.CompanyUserHiringTeamLink;
import performa.orm.HiringTeam;
import performa.orm.types.RoleType;

/**
 * This is almost similar class to OAuthCallbackDecorator,
 * except setup user extensions using redirect URL before redirect request.
 * 
 * @see OAuthCallbackDecorator
 */
public class PerformaOAuthCallbackDecorator implements ServletDecorator, InitialisationParticipant
{
    private             LoggingArea                 LOG                         = LoggingArea.createLoggingArea("OAuthCallbackDecorator");
    private             TransactionServicesFactory  servicesFactory;
    
    public static final String                      REDIRECT_URL_ATTRIB_NAME    = "oauth.returnURL";
    public static final String                      TOKEN_ATTRIB_NAME           = "oauth.token";
    public static final String                      ACCESS_DENY_URL_ATTRIB_NAME = "oauth.accessDenyURL";
    public static final String                      ACCESS_DENY_ERROR_KEY       = "oauth.accessDenyError";
   
    /**
     * Map <Callback Service, Callback Handler>
     */
    public Map handlerMap;

    @Override
    public void processRequest (ServletDecoratorConfig config, BaseHttpServletRequest request, BaseHttpServletResponse response) throws Exception
    {
        String              callbackURL     = request.getServletPath();
        HttpSession         session         = request.getSession();

        try
        {
            
            LogMgr.log(LOG, LogLevel.DEBUG1, "Entering into OAuthCallbackDecorator");
            if(handlerMap != null && handlerMap.containsKey(callbackURL))
            {
                LogMgr.log(LOG, LogLevel.PROCESSING1, "Catching in OAuthCallbackDecorator RE for callback :: ", callbackURL);
                
                BaseOAuthLoginHandler loginHandler = (BaseOAuthLoginHandler)handlerMap.get(callbackURL);
                
                // Validate request is authorized or not.
                loginHandler.validateRequest(request);
                
                
                // Create or Get Login user from oauth process
                ObjectTransaction   transaction =   new ObjectTransaction (servicesFactory);
                
                String              fullURL     =   (String)session.getAttribute(REDIRECT_URL_ATTRIB_NAME);
                
                try
                {
                    SecUser secUser = loginHandler.getOrCreateSecuser(request, transaction);
                    
                    if(fullURL.contains(WebUtils.APPLICANT_PORTAL) && !secUser.hasPrivilege(Utils.PRIV_ACCESS_ADMIN_PORTAL))
                    {
                        secUser.addRole(Role.searchNAME(secUser.getTransaction(), Utils.ROLE_APPLICANT));
                        
                        Candidate   candidate   =   secUser.getExtensionOrCreate(Candidate.REFERENCE_Candidate);

                        candidate.setUser(secUser);
                    }
                    else if(fullURL.contains(WebUtils.ADMIN_PORTAL) && !secUser.hasPrivilege(Utils.PRIV_ACCESS_APPLICANT_PORTAL))
                    {
                        secUser.addRole(Role.searchNAME(secUser.getTransaction(), Utils.ROLE_CLIENT));
                        secUser.addRole(Role.searchNAME(secUser.getTransaction(), Utils.ROLE_COMPANY_ADMIN));
                        
                        CompanyUser companyUser =   secUser.getExtensionOrCreate(CompanyUser.REFERENCE_CompanyUser);
                        
                        if(companyUser.getStatus()==ObjectStatus.NEW)
                        {
                            Company   company    =   Company.createCompany(transaction);
                            
                            company.setCompanyName("");
                            company.setAddedByUser(companyUser);
                            companyUser.setCompany(company);
        
                            HiringTeam hiringTeam   =   company.getHiringTeamsAt(0);
                            hiringTeam.setAddedByUser(companyUser);
                            hiringTeam.setHiringTeamName("");
                            companyUser.setDefaultHiringTeam(hiringTeam);

                            CompanyUserHiringTeamLink link  =   CompanyUserHiringTeamLink.createCompanyUserHiringTeamLink(transaction);
                            
                            link.setCompanyUser(companyUser);
                            link.setHiringTeam(hiringTeam);
                            link.setRole(RoleType.OWNER);
                        }
                        else if(companyUser.getIsEmailChanged()==Boolean.TRUE)
                        {
                            companyUser.changeEmail(request);
                        }
                    }
                    
                    transaction.commit();
                    transaction.commitResources();
                    
                    // Store user in session for login.
                    session.setAttribute(SecUser.SEC_USER_ID, secUser);
                    session.setAttribute(SessionSecUserDecorator.REFRESH_SECURITY, Boolean.TRUE);
                }
                finally
                {
                    transaction.releaseResources();
                }
                
                
                LogMgr.log(LOG, LogLevel.PROCESSING1, "Redirecting user to next page after login. ", fullURL);
                
                
                if(fullURL == null || fullURL.isEmpty())
                {
                    LogMgr.log(LOG, LogLevel.PROCESSING1, "Can't find returnURL in Session.");
                    config.forwardRequest(request, response);
                    return;
                }
                
                
                // Redirect user on next page after login
                request.setAttribute ("DecoratorFilter.TERMINATE", "YES");
                LogMgr.log(LOG, LogLevel.PROCESSING1, "Redirecting form OAuthCallbackDecorator ON :: " + fullURL);
                response.sendRedirect(fullURL);
                return;
            }
            LogMgr.log(LOG, LogLevel.DEBUG1, "Exit from OAuthCallbackDecorator");
            config.forwardRequest(request, response);
        }
        catch(Exception e)
        {
            if (e instanceof BaseOAuthLoginHandler.LoadProfileException)
            {
                LogMgr.log(LOG, LogLevel.PROCESSING1, e);
                
                Map<String, String> paramsMap   = getURLParams((String)session.getAttribute(REDIRECT_URL_ATTRIB_NAME));
                
                if (StringUtils.subBlanks(paramsMap.get(ACCESS_DENY_URL_ATTRIB_NAME)) == null)
                {
                    throw new NestedException(e);
                }
                
                request.setAttribute ("DecoratorFilter.TERMINATE", "YES");
                session.setAttribute(ACCESS_DENY_ERROR_KEY, e.getMessage());
                response.sendRedirect(paramsMap.get(ACCESS_DENY_URL_ATTRIB_NAME));
                return;
            }
            
            LogMgr.log(LOG, LogLevel.SYSTEMERROR1, e, "Error occurred during decorator filter.");
            throw NestedException.wrap(e);
        }
        finally
        {
            session.removeAttribute(TOKEN_ATTRIB_NAME);
            session.removeAttribute(REDIRECT_URL_ATTRIB_NAME);
        }
    }

    @Override
    public void init(ParticipantInitialisationContext context) throws InitialisationException
    {
        context.setObject(this);
        handlerMap = context.getChildMap("Handler");
    }
    
    
    /**
     * Get request parameters as a map, from given URL.
     * 
     * @param urlString
     * @return 
     */
    public static Map<String, String> getURLParams(String urlString)
    {
        Map<String,String>      urlParamsMap    = new HashMap<String, String>();
        URL                     url;
        
        try
        {
            url     = new URL(urlString);
        }
        catch (MalformedURLException ex)
        {
            LogMgr.log(LoggingArea.ALL, LogLevel.SYSTEMERROR1, ex, "\n Malformed URL:" + urlString);
            return urlParamsMap;
        }
        
        String[]                params          = url.getQuery() != null ? url.getQuery().split("&") : new String[] {};
        
        for (String param: params)
        {
            String  key = param.substring(0, param.indexOf('='));
            
            urlParamsMap.put( key, param.substring(param.indexOf('=') + 1));
        }
        
        return urlParamsMap;
    }
}

