/****************************************************************************
 * 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.TaskDashboard;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.xml.bind.DatatypeConverter;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;

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.TaskDashboard.types.AccessTokenJson;
import com.avaya.zephyr.services.TaskDashboard.types.ResopnseJson;
import com.avaya.zephyr.services.sample_services.Authorization.TaskDashboard.provider.EndpointProvider;
import com.avaya.zephyr.services.sample_services.Authorization.TaskDashboard.provider.HttpUtilProvider;
import com.avaya.zephyr.services.sample_services.Authorization.TaskDashboard.provider.ProviderFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

/**
 * TaskDashboard is the entry point for REST request from authorization client UI to get user token and task list for client
 * 
 * @author Avaya Inc
 * 
 */
@Path("/resource")
public final class TaskDashboard
{

    private static final String ACCEPT_HEADER = MediaType.APPLICATION_JSON;
    private static final String AUTHORIZATION_BEARER = "Bearer ";
    private static final String AUTHENTICATION_HEADER = "Authorization";
    private static final String AUTHENTICATION_TYPE = "Basic";
    private final HttpUtilProvider httpUtilProvider;
    private final Logger logger;
    private EndpointProvider provider;

    public TaskDashboard()
    {
        this(ProviderFactory.INSTANCE.getHttpUtilProvider(), Logger.getLogger(TaskDashboard.class), ProviderFactory.INSTANCE
                .getEndpointProvider());
    }

    TaskDashboard(final HttpUtilProvider httpUtilProvider, final Logger logger, final EndpointProvider provider)
    {
        this.httpUtilProvider = httpUtilProvider;
        this.logger = logger;
        this.provider = provider;
    }

    /**
     * getTokenForUser - get access toke for a particular user
     * @param httpServletRequest, which contains user name and password in Authorization header
     * @return Response, access token, error response when unable to get token
     */
    @Path("/gettokenforuser")
    @GET
    @Produces(
    { MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN })
    public Response getTokenForUser(@Context final HttpServletRequest httpServletRequest)
    {
        final String authCredentials = httpServletRequest.getHeader(AUTHENTICATION_HEADER);
        String username;
        String password;
        if (authCredentials != null && authCredentials.startsWith(AUTHENTICATION_TYPE))
        {
            final String encodedCredentials = authCredentials.substring(AUTHENTICATION_TYPE.length()).trim();
            final String decodedCredentials = new String(
                    DatatypeConverter.parseBase64Binary(encodedCredentials));
            final String[] credentials = decodedCredentials.split(":", 2);
            username = credentials[0];
            password = credentials[1];
        }
        else
        {
            logger.error("request un-authorized");
            return Response.status(Status.UNAUTHORIZED).build();
        }
        final List<String> scopeList = new ArrayList<>();
        scopeList.add("create");
        scopeList.add("list");
        scopeList.add("delete");
        scopeList.add("update");
        try
        {
            final AccessToken accessToken = AuthorizationClientHelper.getAccessTokenForUser(username, password, scopeList);
            logger.finer("access token  = " + accessToken);
            return buildTokenResponse(accessToken);
        }
        catch (final AuthorizationHelperException e)
        {
            logger.error("Caught AuthorizationHelperException: ", e);
            return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(e.getMessage())
                    .type(MediaType.TEXT_PLAIN).build();
        }
        catch (final HttpResponseException e)
        {
            logger.error("Caught Http Response exception: ", e);
            return Response.status(e.getStatusCode()).entity(e.getStatusMessage()).type(MediaType.TEXT_PLAIN)
                    .build();
        }
    }

    /**
     * getResource - get the tasks for all client get a token from client helper, using the token make a request to authorization resource
     * to get tasks for all users
     * 
     * @return Response with List&lt;Task&gt;, Error response when no grants are assigned to client or authorization resource responds with error
     */
    @Path("/getresource")
    @GET
    @Produces(
    { MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON })
    public Response getResource()
    {
        final String resourecUlr = provider.getResourceEndpoint();
        if (logger.isFinerEnabled())
        {
            logger.finer("resource URL = " + resourecUlr);
        }
        if (StringUtils.isEmpty(resourecUlr))
        {
            return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity("resource url is empty")
                    .type(MediaType.TEXT_PLAIN).build();
        }
        String responseAsString = null;
        final HttpGet httpGetResource = new HttpGet(resourecUlr);
        httpGetResource.addHeader("Content-Type", MediaType.APPLICATION_JSON);
        httpGetResource.addHeader("Accept", ACCEPT_HEADER);
        final AccessToken accessToken = getToken();
        if (null == accessToken)
        {
            logger.error("access token is null");
            return Response.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR).entity("No grants assigned to client")
                    .type(MediaType.TEXT_PLAIN).build();
        }
        httpGetResource.addHeader("Authorization", AUTHORIZATION_BEARER + accessToken.toString());
        CloseableHttpResponse response = null;
        final CloseableHttpClient client = httpUtilProvider.getHttpClient();
        try
        {
            response = client.execute(httpGetResource);
            responseAsString = httpUtilProvider.getHttpResponseAsString(response);
            if (logger.isFinerEnabled())
            {
                logger.finer("responseAsString   = " + responseAsString);
            }
            final int status = response.getStatusLine().getStatusCode();
            logger.info("status   = " + status);
            if (status != Status.OK.getStatusCode())
            {
                logger.error("error in getting tasks ");
                return Response.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR).entity("error in getting tasks: status" + status)
                        .type(MediaType.TEXT_PLAIN).build();
            }
            final Gson gson = new Gson();
            return Response.status(HttpStatus.SC_OK).entity(gson.fromJson(responseAsString, ResopnseJson.class).getTasks())
                    .type(MediaType.APPLICATION_JSON).build();
        }
        catch (final IOException e)
        {
            logger.error("exception in get resource " + e.getMessage());
            return Response.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR).entity(e.getMessage())
                    .type(MediaType.TEXT_PLAIN).build();
        }
    }

    /**
     * buildTokenResponse - build json from AccessToken
     * 
     * @param accessToken
     * @return Response
     */
    private Response buildTokenResponse(final AccessToken accessToken)
    {
        final AccessTokenJson accessTokenAsJson = new AccessTokenJson();
        accessTokenAsJson.setAccessToken(accessToken.toString());
        accessTokenAsJson.setExpiresIn(accessToken.getExpiresIn());
        accessTokenAsJson.setScopes(accessToken.getScopes());

        final Gson gson = new GsonBuilder().setPrettyPrinting().create();
        return Response.ok(gson.toJson(accessTokenAsJson), MediaType.APPLICATION_JSON).build();
    }

    /**
     * getToken - retrieve access token from helper library
     * 
     * @return AccessToken
     */
    private AccessToken getToken()
    {
        AccessToken accessToken = null;
        try
        {
            accessToken = AuthorizationClientHelper.getAccessToken(Arrays.asList("listall"));
            return accessToken;
        }
        catch (final AuthorizationHelperException | HttpResponseException e)
        {
            logger.error("Caught AuthorizationHelperException: ", e);
            return accessToken;
        }
    }
}
