/****************************************************************************
 * Copyright Avaya Inc., All Rights Reserved.
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF Avaya Inc.
 * The copyright notice above does not evidence any actual or intended publication of such source code.
 * Some third-party source code components may have been modified from their original versions by Avaya Inc.
 * The modifications are Copyright Avaya Inc., All Rights Reserved.
 * Avaya Confidential & Restricted. May not be distributed further without written permission of
 * the Avaya owner.
 ****************************************************************************/
package com.avaya.zephyr.services.sample_services.Authorization;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Calendar;
import java.util.Date;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.URIBuilder;

import com.avaya.collaboration.authorization.AuthorizationHelperException;
import com.avaya.collaboration.authorization.client.AccessToken;
import com.avaya.collaboration.authorization.client.AuthorizationClientHelper;
import com.avaya.collaboration.authorization.client.HttpResponseException;
import com.avaya.collaboration.util.logger.Logger;
import com.avaya.zephyr.services.sample_services.Authorization.types.CurrentUser;
import com.avaya.zephyr.services.sample_services.Authorization.types.UserSession;
import com.avaya.zephyr.services.sample_services.Authorization.util.ServiceAttributeStore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class AccessTokenCookieFilter implements Filter
{
    private static final String AUTH_CODE_GRANT_TYPE = "code";
    private final Logger logger;

    public AccessTokenCookieFilter()
    {
        this(Logger.getLogger(AccessTokenHeaderFilter.class));
    }

    AccessTokenCookieFilter(final Logger logger)
    {
        this.logger = logger;
    }

    @Override
    public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain)
            throws IOException,
            ServletException
    {
        if (hasSessionCookie(servletRequest))
        {
            logger.finer("AccessTokenCookieFilter: cookie present, calling doFilter()");
            filterChain.doFilter(servletRequest, servletResponse);
        }
        else if (hasAuthorizationCode(servletRequest))
        {
            logger.finer("AccessTokenCookieFilter: code present, getting access token");
            fetchAccessTokenAndRedirectWithCookie(servletRequest, servletResponse);
        }
        else
        {
            logger.warn("Request not authenticated - no authorization code found.");
            ((HttpServletResponse) servletResponse).sendError(HttpServletResponse.SC_UNAUTHORIZED, "No authorization code");
        }
    }

    private boolean hasSessionCookie(ServletRequest servletRequest)
    {
        final HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        final Cookie[] cookies = httpServletRequest.getCookies();
        if (cookies != null && cookies.length > 0)
        {
            for (int i = 0; i < cookies.length; i++)
            {
                if (cookies[i].getName().equals(ServiceAttributeStore.getInstance().getSessionCookieName()))
                {
                    return true;
                }
            }
            return false;
        }
        else
        {
            return false;
        }
    }

    private boolean hasAuthorizationCode(final ServletRequest servletRequest)
    {
        return StringUtils.isNotBlank(servletRequest.getParameter(AUTH_CODE_GRANT_TYPE));
    }

    private void fetchAccessTokenAndRedirectWithCookie(final ServletRequest servletRequest, final ServletResponse servletResponse)
            throws IOException
    {
        try
        {
            final AccessToken accessToken = AuthorizationClientHelper.getAccessTokenForUser(servletRequest);
            final String userSession = buildUserSession(accessToken);

            redirectWithCookie(userSession, accessToken.getExpiresIn(), servletRequest, servletResponse);
        }
        catch (AuthorizationHelperException | HttpResponseException e)
        {
            logger.warn("ProcessAuthorizationCode caught exception: " + e);
            ((HttpServletResponse) servletResponse).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
        }
    }

    private String buildUserSession(final AccessToken accessToken) throws JsonProcessingException
    {
        final CurrentUser currentUser = new CurrentUser();
        currentUser.setUsername(accessToken.getSubject());
        currentUser.setAuthdata(accessToken.toString());
        currentUser.setExpiry(getExpiryTime(accessToken.getExpiresIn()));

        final UserSession userSession = new UserSession();
        userSession.setCurrentUser(currentUser);

        return new ObjectMapper().writeValueAsString(userSession);
    }

    private void redirectWithCookie(final String userSession, final int cookieExpiry, final ServletRequest servletRequest,
            final ServletResponse servletResponse)
            throws IOException
    {
        final HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        final HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

        final Cookie sessionCookie = new Cookie(ServiceAttributeStore.getInstance().getSessionCookieName(), userSession);
        sessionCookie.setMaxAge(cookieExpiry);
        sessionCookie.setSecure(true);

        try
        {
            final URI clientRedirectUri = new URIBuilder()
                    .setScheme(httpServletRequest.getHeader("Scheme"))
                    .setHost(httpServletRequest.getHeader("Host"))
                    .setPath(ServiceAttributeStore.getInstance().getClientRedirectionPath())
                    .build();

            logger.finer("AccessTokenCookieFilter: redirecting..");
            httpServletResponse.addCookie(sessionCookie);
            httpServletResponse.sendRedirect(clientRedirectUri.toString());
        }
        catch (final URISyntaxException e)
        {
            logger.warn("RedirectToDashboard caught exception: " + e);
        }
    }

    private String getExpiryTime(final int expiryInSeconds)
    {
        final Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.SECOND, expiryInSeconds);
        return String.valueOf(cal.getTimeInMillis());
    }

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException
    {
        logger.finer("AccessTokenCookieFilter : init");
    }

    @Override
    public void destroy()
    {
        logger.finer("AccessTokenCookieFilter : destroy");
    }
}
