/*
 * 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 com.scytl.products.ov.commons.infrastructure.remote.client.RetrofitException;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;

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.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 java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

import com.scytl.products.ov.ag.services.infrastructure.remote.admin.KeyTranslationAdminClient;
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.beans.domain.model.keytranslation.ElectionEventTranslations;
import com.scytl.products.ov.commons.infrastructure.remote.client.InputStreamTypedOutput;
import com.scytl.products.ov.commons.beans.domain.model.keytranslation.VotingCardTranslationList;
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.tracking.TrackIdGenerator;
import com.scytl.products.ov.commons.util.PropertiesFileReader;

/**
 * Web service for handling electoral data resource.
 */
@Stateless(name = "kt-KeyTranslationResource")
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Path(KeyTranslationResource.RESOURCE_PATH)
public class KeyTranslationResource {

    static final String GET_ELECTION_EVENT_ID =
        "/electionevent/tenant/{tenantId}/alias/{alias}";

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

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

    static final String GET_VOTING_CARD_ID =
        "/votingcard/tenant/{tenantId}/electionevent/{electionEventId}/alias/{alias}";

    static final String UPLOAD_VOTING_CARD_TRANSLATION = "/votingcard/tenant/{tenantId}";

    static final String GET_BALLOT_BOX_TRANSLATION_BY_ALIAS =
        "/ballotbox/tenant/{tenantId}/electionevent/{electionEventId}/alias/{alias}";

    static final String GET_BALLOT_BOX_TRANSLATION_BY_ID =
        "/ballotbox/tenant/{tenantId}/electionevent/{electionEventId}/id/{bbid}";

    static final String UPLOAD_BALLOT_BOX_TRANSLATION = "/ballotbox/tenant/{tenantId}";

    static final String RESOURCE_PATH = "/kt";

    private static final String PARAMETER_VALUE_TENANT_ID = "tenantId";

    private static final String PARAMETER_VALUE_ELECTION_EVENT_ID = "electionEventId";

    private static final String PARAMETER_VALUE_ALIAS = "alias";

    private static final String PARAMETER_VALUE_BALLOT_BOX_ID = "bbid";

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

    static final String PATH_ELECTION_EVENT = PROPERTIES.getPropertyValue("KEY_TRANSLATION_EE_DATA_PATH");

    static final String PATH_VOTING_CARD = PROPERTIES.getPropertyValue("KEY_TRANSLATION_VC_DATA_PATH");

    static final String PATH_BALLOT_BOX = PROPERTIES.getPropertyValue("KEY_TRANSLATION_BB_DATA_PATH");

    private final XForwardedForFactory xForwardedForFactory = XForwardedForFactoryImpl.getInstance();

    private KeyTranslationAdminClient keyTranslationAdminClient;

    private TrackIdGenerator trackIdGenerator;

    private static final Logger LOG = LoggerFactory.getLogger(KeyTranslationResource.class);
    
    @Inject
    KeyTranslationResource(KeyTranslationAdminClient keyTranslationAdminClient,
                           TrackIdGenerator trackIdGenerator) {
        this.keyTranslationAdminClient = keyTranslationAdminClient;
        this.trackIdGenerator = trackIdGenerator;
    }

    @Path(GET_ELECTION_EVENT_ID)
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getElectionEventId(@PathParam(PARAMETER_VALUE_TENANT_ID) final String tenantId,
            @PathParam(PARAMETER_VALUE_ALIAS) final String alias, @Context final HttpServletRequest request)
            throws IOException {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        try {
        	retrofit2.Response<ResponseBody> processResponse = RetrofitConsumer.executeCall(
        			keyTranslationAdminClient
                    .getElectionEventTranslation(PATH_ELECTION_EVENT, tenantId, alias, xForwardedFor, trackingId));
        	String jsonString = getJsonFromBody(processResponse);
            return Response.ok().entity(jsonString).build();
        } catch (RetrofitException rfE) {
			LOG.error("Error trying to get election event Id.", rfE);
            return Response.status(Response.Status.NOT_FOUND).entity(rfE.getHttpCode()).build();
		}

    }

    @Path(UPLOAD_ELECTION_EVENT_TRANSLATION)
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response uploadElectionEventTranslation(@PathParam(PARAMETER_VALUE_TENANT_ID) final String tenantId,
            final ElectionEventTranslations listEET, @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();

        String originator = request.getHeader(RestClientInterceptor.HEADER_ORIGINATOR);
        String signature = request.getHeader(RestClientInterceptor.HEADER_SIGNATURE);
		try (ResponseBody responseBody = RetrofitConsumer
				.processResponse(keyTranslationAdminClient.uploadElectionEventTranslation(PATH_ELECTION_EVENT, tenantId,
						xForwardedFor, trackingId, originator, signature, listEET))) {
			return Response.ok().build();
		} catch (RetrofitException rfE) {
			LOG.error("Error trying to upload election event translation.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }

    /**
     * Returns an object containing the alias (public identifier) for the requested election event.
     *
     * @param tenantId
     *            - the tenant identifier.
     * @param electionEventId
     *            - the election event internal identifier.
     * @return http status 200 with ElectionEventTranslation object in json format if resource is found. Otherwise, http
     *         status 404 if not found.
     * @throws IOException
     *             if there are errors during conversion of vote to json format.
     */
    @Path(GET_ELECTION_EVENT_ALIAS)
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getElectionEventAlias(@NotNull @PathParam(PARAMETER_VALUE_TENANT_ID) String tenantId,
            @NotNull @PathParam(PARAMETER_VALUE_ELECTION_EVENT_ID) String electionEventId,
            @Context final HttpServletRequest request) throws IOException {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        try {
        	retrofit2.Response<ResponseBody> processResponse = RetrofitConsumer.executeCall(
        			keyTranslationAdminClient.getElectionEventAliasTranslation(
        	                PATH_ELECTION_EVENT, tenantId, electionEventId, xForwardedFor, trackingId));
        	String jsonString = getJsonFromBody(processResponse);
            return Response.ok().entity(jsonString).build();
        } catch (RetrofitException rfE) {
			LOG.error("Error trying to get election event alias.", rfE);
            return Response.status(Response.Status.NOT_FOUND).entity(rfE.getHttpCode()).build();
		}
    }

    @Path(GET_VOTING_CARD_ID)
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getVotingCardId(@PathParam(PARAMETER_VALUE_TENANT_ID) final String tenantId,
            @PathParam(PARAMETER_VALUE_ELECTION_EVENT_ID) final String electionEventId,
            @PathParam(PARAMETER_VALUE_ALIAS) final String alias, @Context final HttpServletRequest request)
            throws IOException {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        try {
        	retrofit2.Response<ResponseBody> processResponse = RetrofitConsumer.executeCall(
        			keyTranslationAdminClient.getVotingCardTranslation(PATH_VOTING_CARD,
        	                tenantId, electionEventId, alias, xForwardedFor, trackingId));
        	String jsonString = getJsonFromBody(processResponse);
            return Response.ok().entity(jsonString).build();
        } catch (RetrofitException rfE) {
			LOG.error("Error trying to get voting card Id.", rfE);
            return Response.status(Response.Status.NOT_FOUND).entity(rfE.getHttpCode()).build();
		}
    }

    @Path(UPLOAD_VOTING_CARD_TRANSLATION)
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response uploadVotingCardTranslation(@PathParam(PARAMETER_VALUE_TENANT_ID) final String tenantId,
    		final VotingCardTranslationList listVCS, @Context final HttpServletRequest request) {

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

        String originator = request.getHeader(RestClientInterceptor.HEADER_ORIGINATOR);
        String signature = request.getHeader(RestClientInterceptor.HEADER_SIGNATURE);
        
		try (ResponseBody responseBody = RetrofitConsumer
				.processResponse(keyTranslationAdminClient.uploadVotingCardTranslation(PATH_VOTING_CARD, tenantId,
						xForwardedFor, trackingId, originator, signature, listVCS))) {
			return Response.ok().build();
		} catch (RetrofitException rfE) {
			LOG.error("Error trying to upload voting card translation.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }

    @Path(GET_BALLOT_BOX_TRANSLATION_BY_ALIAS)
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getBallotBoxTranslationByAlias(@PathParam(PARAMETER_VALUE_TENANT_ID) final String tenantId,
                                                   @PathParam(PARAMETER_VALUE_ELECTION_EVENT_ID) final String electionEventId,
                                                   @PathParam(PARAMETER_VALUE_ALIAS) final String alias,
                                                   @Context final HttpServletRequest request)
        throws IOException {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        try {
        	retrofit2.Response<ResponseBody> processResponse = RetrofitConsumer.executeCall(
        			keyTranslationAdminClient.getBallotBoxTranslationByAlias(PATH_BALLOT_BOX,
        	                tenantId, electionEventId, alias, xForwardedFor, trackingId));
        	String jsonString = getJsonFromBody(processResponse);
            return Response.ok().entity(jsonString).build();
        } catch (RetrofitException rfE) {
			LOG.error("Error trying to get ballot box translation by alias.", rfE);
            return Response.status(Response.Status.NOT_FOUND).entity(rfE.getHttpCode()).build();
		}
    }

    @Path(GET_BALLOT_BOX_TRANSLATION_BY_ID)
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getBallotBoxTranslationById(@PathParam(PARAMETER_VALUE_TENANT_ID) final String tenantId,
                                                   @PathParam(PARAMETER_VALUE_ELECTION_EVENT_ID) final String electionEventId,
                                                   @PathParam(PARAMETER_VALUE_BALLOT_BOX_ID) final String bbid,
                                                   @Context final HttpServletRequest request)
        throws IOException {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        try {
        	retrofit2.Response<ResponseBody> processResponse = RetrofitConsumer.executeCall(
        			keyTranslationAdminClient.getBallotBoxTranslationByBallotBoxId(PATH_BALLOT_BOX,
        	                tenantId, electionEventId, bbid, xForwardedFor, trackingId));
        	String jsonString = getJsonFromBody(processResponse);
            return Response.ok().entity(jsonString).build();
        } catch (RetrofitException rfE) {
			LOG.error("Error trying to get ballot box translation by Id.", rfE);
            return Response.status(Response.Status.NOT_FOUND).entity(rfE.getHttpCode()).build();
		}
    }

    @Path(UPLOAD_BALLOT_BOX_TRANSLATION)
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response uploadBallotBoxTranslation(@PathParam(PARAMETER_VALUE_TENANT_ID) final String tenantId,
                                               @NotNull final InputStream info,
                                               @Context final HttpServletRequest request) {

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

        String originator = request.getHeader(RestClientInterceptor.HEADER_ORIGINATOR);
        String signature = request.getHeader(RestClientInterceptor.HEADER_SIGNATURE);

		RequestBody body = new InputStreamTypedOutput(MediaType.APPLICATION_JSON, info);
		try (ResponseBody responseBody = RetrofitConsumer
				.processResponse(keyTranslationAdminClient.uploadBallotBoxTranslation(PATH_BALLOT_BOX, tenantId,
						xForwardedFor, trackingId, originator, signature, body))) {
			return Response.ok().build();
		} catch (RetrofitException rfE) {
			LOG.error("Error trying to upload ballot box translation.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }
    
    private String getJsonFromBody(retrofit2.Response<ResponseBody> response) throws IOException {
    	String jsonString = "";
    	ResponseBody body = response.body();
    	if(body==null || body.contentLength()==0){
    		return jsonString;
    	}
        InputStream byteStream = body.byteStream();

        try (BufferedReader buffer = new BufferedReader(new InputStreamReader(byteStream))) {
            jsonString = buffer.lines().collect(Collectors.joining("\n"));
        }
        return jsonString;
    }
}
