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

import java.io.IOException;
import java.io.InputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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.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 com.google.gson.JsonObject;
import com.scytl.products.ov.ag.services.infrastructure.remote.admin.ElectionInformationAdminClient;
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 for handling ballot box information.
 */
@Stateless(name = "ag-BallotBoxResource")
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Path(BallotBoxResource.RESOURCE_PATH)
public class BallotBoxResource {

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

    static final String GET_ENCRYPTED_BALLOT_BOX_CSV = BALLOT_BOX_URL + "/ballotbox/{ballotBoxId}";

    static final String CHECK_IF_BALLOT_BOX_IS_EMPTY = BALLOT_BOX_URL + "/ballotbox/{ballotBoxId}/status";

    static final String CHECK_IF_BALLOT_BOX_IS_AVAILABLE = BALLOT_BOX_URL + "/ballotbox/{ballotBoxId}/available";

    static final String BLOCK_BALLOT_BOXES = BALLOT_BOX_URL + "/block";

    static final String UNBLOCK_BALLOT_BOXES = BALLOT_BOX_URL + "/unblock";

    static final String RESOURCE_PATH = "/ei/ballotboxes";

    // The name of the query parameter tenantId
    private static final String QUERY_PARAMETER_TENANT_ID = "tenantId";

    // The name of the query parameter electionEventId
    private static final String QUERY_PARAMETER_ELECTION_EVENT_ID = "electionEventId";

    // The name of the query parameter ballotBoxId
    private static final String QUERY_PARAMETER_BALLOT_BOX_ID = "ballotBoxId";

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

    // The path to the resource ballot box.
    static final String BALLOT_BOX_PATH = PROPERTIES.getPropertyValue("BALLOT_BOX_PATH");

    // The path to the resource ballot box status.
    static final String BALLOT_BOX_STATUS_PATH = PROPERTIES.getPropertyValue("BALLOT_BOX_STATUS_PATH");

    // The path to the resource ballot box availability status.
    private static final String BALLOT_BOX_AVAILABILITY_STATUS_PATH =
        PROPERTIES.getPropertyValue("BALLOT_BOX_AVAILABILITY_STATUS_PATH");

    private final XForwardedForFactory xForwardedForFactory = XForwardedForFactoryImpl.getInstance();

    private ElectionInformationAdminClient electionInformationAdminClient;

    private TrackIdGenerator trackIdGenerator;
    
    private static final Logger LOG = LoggerFactory.getLogger(BallotBoxResource.class);
    
    @Inject
    BallotBoxResource(ElectionInformationAdminClient electionInformationAdminClient,
            TrackIdGenerator trackIdGenerator) {
        this.electionInformationAdminClient = electionInformationAdminClient;
        this.trackIdGenerator = trackIdGenerator;
    }

    @GET
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    @Path(GET_ENCRYPTED_BALLOT_BOX_CSV)
    public Response getEncryptedBallotBox(@PathParam(QUERY_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(QUERY_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @PathParam(QUERY_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(electionInformationAdminClient.getEncryptedBallotBox(BALLOT_BOX_PATH,
                    tenantId, electionEventId, ballotBoxId, xForwardedFor, trackingId, originator, signature));

			return Response.ok().entity(response.byteStream()).build();

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

    /**
     * Returns the result of validate if all ballot boxes for a given tenant,
     * election event and ballot box are empty.
     *
     * @param tenantId
     *            - the tenant identifier.
     * @param electionEventId
     *            - the election event identifier.
     * @param ballotBoxId
     *            - the ballot box identifier.
     * @return Returns the result of the validation.
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path(CHECK_IF_BALLOT_BOX_IS_EMPTY)
    public Response checkIfBallotBoxIsEmpty(@PathParam(QUERY_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(QUERY_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @PathParam(QUERY_PARAMETER_BALLOT_BOX_ID) final String ballotBoxId,
            @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();

        try {
            JsonObject processResponse =
                RetrofitConsumer.processResponse(electionInformationAdminClient.checkIfBallotBoxIsEmpty(
                    BALLOT_BOX_STATUS_PATH, tenantId, electionEventId, ballotBoxId, xForwardedFor, trackingId));
            return Response.ok().entity(processResponse.toString()).build();
        } catch (RetrofitException rfE) {
        	LOG.error("Error trying to check if ballot box is empty.", rfE);
            return Response.status(rfE.getHttpCode()).build();
        }

    }

    /**
     * Returns the result of validate if all ballot boxes for a given tenant,
     * election event and ballot box is available.
     *
     * @param tenantId
     *            - the tenant identifier.
     * @param electionEventId
     *            - the election event identifier.
     * @param ballotBoxId
     *            - the ballot box identifier.
     * @return Returns the result of the validation.
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path(CHECK_IF_BALLOT_BOX_IS_AVAILABLE)
    public Response checkIfBallotBoxIsAvailable(@PathParam(QUERY_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(QUERY_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @PathParam(QUERY_PARAMETER_BALLOT_BOX_ID) final String ballotBoxId,
            @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        try {
            JsonObject processResponse = RetrofitConsumer.processResponse(
                electionInformationAdminClient.checkIfBallotBoxIsAvailable(BALLOT_BOX_AVAILABILITY_STATUS_PATH,
                    tenantId, electionEventId, ballotBoxId, xForwardedFor, trackingId));
            return Response.ok().entity(processResponse.toString()).build();
        } catch (RetrofitException rfE) {
        	LOG.error("Error trying to check if ballot box is available.", rfE);
            return Response.status(rfE.getHttpCode()).build();
        }
    }

    /**
     * Block ballot box
     * 
     * @param tenantId
     *            the tenant identifier
     * @param electionEventId
     *            the election event identifier
     * @param ballotBoxes
     *            list of ballot box identifiers
     * @param request
     *            the Http Servlet Request
     */
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Path(BLOCK_BALLOT_BOXES)
    public Response blockBallotBoxes(@PathParam(QUERY_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(QUERY_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @NotNull final InputStream ballotBoxes, @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackId = trackIdGenerator.generate();
        String originator = request.getHeader(RestClientInterceptor.HEADER_ORIGINATOR);
        String signature = request.getHeader(RestClientInterceptor.HEADER_SIGNATURE);
        RequestBody body = new InputStreamTypedOutput(MediaType.APPLICATION_JSON, ballotBoxes);

		try (ResponseBody response = RetrofitConsumer.processResponse(electionInformationAdminClient.blockBallotBoxes(
				BALLOT_BOX_PATH, tenantId, electionEventId, body, originator, signature, xForwardedFor, trackId))) {
			return Response.ok().build();
		} catch (RetrofitException rfE) {
			LOG.error("Error trying to block ballot boxes.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }

    /**
     * Unblock ballot box
     * 
     * @param tenantId
     *            the tenant identifier
     * @param electionEventId
     *            the election event identifier
     * @param ballotBoxes
     *            list of ballot box identifiers
     * @param request
     *            the Http Servlet Request
     */
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Path(UNBLOCK_BALLOT_BOXES)
    public Response unblockBallotBoxes(@PathParam(QUERY_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(QUERY_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @NotNull final InputStream ballotBoxes, @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackId = trackIdGenerator.generate();

        String originator = request.getHeader(RestClientInterceptor.HEADER_ORIGINATOR);
        String signature = request.getHeader(RestClientInterceptor.HEADER_SIGNATURE);
        RequestBody body = new InputStreamTypedOutput(MediaType.APPLICATION_JSON, ballotBoxes);
        
		try (ResponseBody response = RetrofitConsumer.processResponse(electionInformationAdminClient.unblockBallotBoxes(
				BALLOT_BOX_PATH, tenantId, electionEventId, body, originator, signature, xForwardedFor, trackId))) {
			return Response.ok().build();
		} catch (RetrofitException rfE) {
			LOG.error("Error trying to unblock ballot boxes.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }
}
