/*
 * 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 com.google.gson.JsonObject;
import com.scytl.products.ov.commons.infrastructure.remote.client.RestClientInterceptor;
import java.io.IOException;
import java.io.InputStream;

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.QueryParam;
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.scytl.products.ov.ag.services.infrastructure.remote.controlcomponents.OrchestratorClient;
import com.scytl.products.ov.ag.ui.ws.rs.RestApplication;
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.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 the process of perform the compute operation
 */
@Stateless(name = "ag-ComputeResource")
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Path(ComputeResource.RESOURCE_PATH)
public class ComputeResource {

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

    static final String COMPUTE_CHOICE_CODES_REQUEST = "computeGenerationContributions";

    static final String COMPUTE_CHOICE_CODES_RETRIEVAL =
        "tenant/{tenantId}/electionevent/{electionEventId}/verificationCardSetId/{verificationCardSetId}/chunkId/{chunkId}/computeGenerationContributions";
    
    static final String PATH_KEYS = "tenant/{tenantId}/electionevent/{electionEventId}/keys";

    static final String CHECK_COMPUTATION_STATUS =
        "tenant/{tenantId}/electionevent/{electionEventId}/verificationCardSetId/{verificationCardSetId}/generationContributions/status";

    // The properties file reader.
    private static final PropertiesFileReader PROPERTIES = PropertiesFileReader.getInstance();

    static final String COMPUTE_PATH = PROPERTIES.getPropertyValue("COMPUTE_CHOICE_CODES_PATH");

    private final XForwardedForFactory xForwardedForFactory = XForwardedForFactoryImpl.getInstance();

    private TrackIdGenerator trackIdGenerator;

    private OrchestratorClient orchestratorClient;

    private static final Logger LOG = LoggerFactory.getLogger(ComputeResource.class);
    
    @Inject
    ComputeResource(OrchestratorClient orchestratorClient, TrackIdGenerator trackIdGenerator) {
        this.trackIdGenerator = trackIdGenerator;
        this.orchestratorClient = orchestratorClient;
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Path(COMPUTE_CHOICE_CODES_REQUEST)
    public Response compute(@NotNull final InputStream computeInput, @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        RequestBody computeInputJson = new InputStreamTypedOutput(MediaType.APPLICATION_JSON, computeInput);
		try (ResponseBody responseBody = RetrofitConsumer.processResponse(orchestratorClient
				.requestComputeChoiceCodes(COMPUTE_PATH, xForwardedFor, trackingId, computeInputJson))) {
			return Response.ok().build();
		} catch (RetrofitException e) {
			LOG.error("Error trying to compute choice codes.", e);
			return Response.status(e.getHttpCode()).build();
		}
    }

    @GET
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    @Path(COMPUTE_CHOICE_CODES_RETRIEVAL)
    public Response compute(@PathParam(RestApplication.PARAMETER_VALUE_TENANT_ID) final String tenantId,
            @PathParam(RestApplication.PARAMETER_VALUE_ELECTION_EVENT_ID) final String electionEventId,
            @PathParam(RestApplication.PARAMETER_VALUE_VERIFICATION_CARD_SET_ID) final String verificationCardSetId,
            @PathParam(RestApplication.PARAMETER_VALUE_CHUNK_ID) final int chunkId,
            @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        try (ResponseBody responseBody =
                RetrofitConsumer.processResponse(orchestratorClient.retrieveComputedChoiceCodes(COMPUTE_PATH, tenantId,
                    electionEventId, verificationCardSetId, chunkId, xForwardedFor, trackingId))) {
        	return Response.ok().entity(responseBody.byteStream()).build();
        } catch (RetrofitException e) {
        	LOG.error("Error trying to compute choice codes.", e);
            return Response.status(e.getHttpCode()).build();
        }      
    }

    @GET
    @Path(CHECK_COMPUTATION_STATUS)
    @Consumes(MediaType.APPLICATION_JSON)
    public Response checkComputationStatus(@PathParam(RestApplication.PARAMETER_VALUE_TENANT_ID) final String tenantId,
            @PathParam(RestApplication.PARAMETER_VALUE_ELECTION_EVENT_ID) final String electionEventId,
            @PathParam(RestApplication.PARAMETER_VALUE_VERIFICATION_CARD_SET_ID) final String verificationCardSetId,
            @QueryParam(RestApplication.PARAMETER_VALUE_CHUNK_COUNT) final int chunkCount,
            @Context final HttpServletRequest request) {

        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        try {
            JsonObject processResponse =
                RetrofitConsumer.processResponse(orchestratorClient.getChoiceCodesComputationStatus(COMPUTE_PATH,
                    tenantId, electionEventId, verificationCardSetId, chunkCount, xForwardedFor, trackingId));
            return Response.ok().entity(processResponse.toString()).build();
        } catch (RetrofitException e) {
        	LOG.error("Error trying to check computation status.", e);
            return Response.status(e.getHttpCode()).build();
        }
    }

    @POST
    @Path(PATH_KEYS)
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response generateChoiceCodesKeys(@PathParam(RestApplication.PARAMETER_VALUE_TENANT_ID) final String tenantId,
            @PathParam(RestApplication.PARAMETER_VALUE_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.generateChoiceCodesKeys(COMPUTE_PATH,
                tenantId, electionEventId, body, originator, signature, xForwardedFor, trackingId));
            return Response.ok().entity(response.toString()).build();

        } catch (RetrofitException e) {
        	LOG.error("Error trying to generate mix decrypt keys.", e);
            return Response.status(e.getHttpCode()).build();
        }
    }

}
