/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.ag.ui.ws.rs.operation.compute;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.scytl.products.ov.ag.services.infrastructure.remote.controlcomponents.OrchestratorClient;
import com.scytl.products.ov.ag.ui.ws.rs.proxy.XForwardedForFactory;
import com.scytl.products.ov.ag.ui.ws.rs.proxy.XForwardedForFactoryImpl;
import com.scytl.products.ov.commons.infrastructure.remote.client.InputStreamTypedOutput;
import com.scytl.products.ov.commons.infrastructure.remote.client.RestClientInterceptor;
import com.scytl.products.ov.commons.infrastructure.remote.client.RetrofitConsumer;
import com.scytl.products.ov.commons.infrastructure.remote.client.RetrofitException;
import com.scytl.products.ov.commons.tracking.TrackIdGenerator;
import com.scytl.products.ov.commons.util.PropertiesFileReader;

import okhttp3.RequestBody;
import okhttp3.ResponseBody;

/**
 * Web service which will handle requests to the MixDec applications.
 */
@Stateless(name = "ag-MixDecResource")
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Path(MixDecResource.RESOURCE_PATH)
public class MixDecResource {

    private static final PropertiesFileReader PROPERTIES = PropertiesFileReader.getInstance();

    static final String MIX_DEC_PATH = PROPERTIES.getPropertyValue("MIX_DEC_PATH");

    static final String RESOURCE_PATH = "/or/mixdec";

    static final String BASE_PATH = "tenant/{tenantId}/electionevent/{electionEventId}";

    static final String PATH_KEYS = BASE_PATH + "/keys";

    static final String PATH_BALLOT_BOXES = BASE_PATH + "/ballotboxes";

    static final String PATH_BALLOT_BOX = BASE_PATH + "/ballotbox/{ballotBoxId}";

    static final String PATH_STATUS = PATH_BALLOT_BOX + "/status";

    static final String PATH_NODE_OUTPUTS = PATH_BALLOT_BOX + "/nodeoutputs";

    static final String PATH_PARAMETER_TENANT_ID = "tenantId";

    static final String PATH_PARAMETER_ELECTION_EVENT_ID = "electionEventId";

    static final String PATH_PARAMETER_BALLOT_BOX_ID = "ballotBoxId";

    private final XForwardedForFactory xForwardedForFactory = XForwardedForFactoryImpl.getInstance();

    private static final Logger LOG = LoggerFactory.getLogger(MixDecResource.class);
    
    @Inject
    private OrchestratorClient orchestratorClient;

    @Inject
    private TrackIdGenerator trackIdGenerator;

    @POST
    @Path(PATH_KEYS)
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response generateMixDecryptKeys(@PathParam(PATH_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(PATH_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @NotNull final InputStream keyGenerationRequestParameters,
            @HeaderParam(RestClientInterceptor.HEADER_ORIGINATOR) final String originator,
            @HeaderParam(RestClientInterceptor.HEADER_SIGNATURE) final String signature,
            @Context final HttpServletRequest request) throws IOException {

        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        RequestBody body = new InputStreamTypedOutput(MediaType.APPLICATION_JSON, keyGenerationRequestParameters);

        JsonObject response = null;
        try {
            response =
                RetrofitConsumer.processResponse(orchestratorClient.generateMixDecKeys(MIX_DEC_PATH,
                    tenantId, electionEventId, body, originator, signature, xForwardedFor, trackingId));
            return Response.ok().entity(response.toString()).build();
        } catch (RetrofitException rfE) {
        	LOG.error("Error trying to generate mix decrypt keys.", rfE);
            return Response.status(rfE.getHttpCode()).build();
        }
    }

    @POST
    @Path(PATH_BALLOT_BOXES)
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response processBallotBoxes(@PathParam(PATH_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(PATH_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @NotNull final InputStream ballotBoxIds,
            @HeaderParam(RestClientInterceptor.HEADER_ORIGINATOR) final String originator,
            @HeaderParam(RestClientInterceptor.HEADER_SIGNATURE) final String signature,
            @Context final HttpServletRequest request) throws IOException {

        RequestBody body = new InputStreamTypedOutput(MediaType.APPLICATION_JSON, ballotBoxIds);

        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();

        JsonArray response = null;
        try {
            response = RetrofitConsumer.processResponse(orchestratorClient
                    .processBallotBoxes(MIX_DEC_PATH, tenantId, electionEventId, body, originator, signature,
                            xForwardedFor, trackingId));

            return Response.ok().entity(response.toString()).build();
        } catch (RetrofitException rfE) {
        	LOG.error("Error trying to process ballot boxes.", rfE);
            return Response.status(rfE.getHttpCode()).build();
        }
    }

    /**
     * @deprecated (SV-5291)
     */
    @GET
    @Path(PATH_BALLOT_BOX)
    @Produces("text/csv")
    @Deprecated // SV-5291
    public Response getProcessedBallotBox(@PathParam(PATH_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(PATH_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @PathParam(PATH_PARAMETER_BALLOT_BOX_ID) final String ballotBoxId,
            @HeaderParam(RestClientInterceptor.HEADER_ORIGINATOR) final String originator,
            @HeaderParam(RestClientInterceptor.HEADER_SIGNATURE) final String signature,
            @Context final HttpServletRequest request) {

        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();

		try (ResponseBody responseBody = RetrofitConsumer
				.processResponse(orchestratorClient.getProcessedBallotBox(MIX_DEC_PATH, tenantId, electionEventId,
						ballotBoxId, originator, signature, xForwardedFor, trackingId))) {

			return Response.ok().entity(new String(responseBody.bytes(), StandardCharsets.UTF_8)).build();
		} catch (RetrofitException rfE) {
			LOG.error("Error trying to get processed ballot box.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		} catch (IOException ioE) {
			LOG.error("Error trying to get processed ballot box.", ioE);
			return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
		}
    }

    @GET
    @Path(PATH_STATUS)
    @Produces(MediaType.APPLICATION_JSON)
    public Response getBallotBoxStatus(@PathParam(PATH_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(PATH_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @PathParam(PATH_PARAMETER_BALLOT_BOX_ID) final String ballotBoxId,
            @HeaderParam(RestClientInterceptor.HEADER_ORIGINATOR) final String originator,
            @HeaderParam(RestClientInterceptor.HEADER_SIGNATURE) final String signature,
            @Context final HttpServletRequest request) {

        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();

        JsonObject response = null;
        try {
            response = RetrofitConsumer.processResponse(orchestratorClient
                    .getBallotBoxStatus(MIX_DEC_PATH, tenantId, electionEventId, ballotBoxId, originator, signature, xForwardedFor, trackingId));

            return Response.ok().entity(response.toString()).build();
        } catch (RetrofitException rfE) {
        	LOG.error("Error trying to get ballot box status.", rfE);
            return Response.status(rfE.getHttpCode()).build();
        }
    }

    /**
     * Gets the outputs of the mixing control component nodes for a particular
     * ballot box.
     * 
     * @param tenantId
     *            the tenant that owns the ballot box
     * @param electionEventId
     *            the election event the ballot box belongs to
     * @param ballotBoxId
     *            the identifier of the ballot box
     * @param originator
     * @param signature
     * @param request
     * @return a multipart response consisting of each node's output as a
     *         separate JSON file
     * @throws IOException
     */
    @GET
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    @Path(PATH_NODE_OUTPUTS)
    public Response getNodeOutputs(@PathParam(PATH_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(PATH_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @PathParam(PATH_PARAMETER_BALLOT_BOX_ID) final String ballotBoxId,
            @HeaderParam(RestClientInterceptor.HEADER_ORIGINATOR) final String originator,
            @HeaderParam(RestClientInterceptor.HEADER_SIGNATURE) final String signature,
            @Context final HttpServletRequest request)
            throws IOException {

        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        
		try {
		    ResponseBody response = RetrofitConsumer
	                .processResponse(orchestratorClient.getMixDecNodeOutputs(MIX_DEC_PATH, tenantId, electionEventId,
	                        ballotBoxId, originator, signature, xForwardedFor, trackingId));
			
			return Response.ok().entity(response.byteStream()).build();
			
        } catch (RetrofitException rfE) {
        	LOG.error("Error trying to get node outputs.", rfE);
            return Response.status(rfE.getHttpCode()).build();
        }
    }
}
