/*
 * 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.google.gson.JsonObject;
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.InputStream;

import com.scytl.products.ov.ag.services.infrastructure.remote.admin.CertificateServiceClient;
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;

@Stateless(name = "ag-CertificatesDataResource")
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Path(CertificatesDataResource.RESOURCE_PATH)
public class CertificatesDataResource {

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

    static final String SAVE_CERTIFICATE_FOR_TENANT = "tenant/{tenantId}/";

    static final String GET_CERTIFICATE = "name/{certificateName}";

    static final String GET_CERTIFICATE_FOR_TENANT = "tenant/{tenantId}/name/{certificateName}";

    static final String CHECK_IF_CERTIFICATE_EXIST = "tenant/{tenantId}/name/{certificateName}/status";

    static final String RESOURCE_PATH = "certificates";

    // 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";

    private static final String QUERY_PARAMETER_CERTIFICATE_NAME = "certificateName";

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

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

    private final XForwardedForFactory xForwardedForFactory = XForwardedForFactoryImpl.getInstance();

    private CertificateServiceClient certificateServiceClient;

    private TrackIdGenerator trackIdGenerator;

    private static final Logger LOG = LoggerFactory.getLogger(CertificatesDataResource.class);
    
    @Inject
    CertificatesDataResource(CertificateServiceClient certificateServiceClient,
                             TrackIdGenerator trackIdGenerator) {
        this.certificateServiceClient = certificateServiceClient;
        this.trackIdGenerator = trackIdGenerator;
    }

    /**
     * Stores a certificate in the registry
     *
     * @param tenantId
     *            - tenant identifier
     * @param electionEventId
     *            - election event identifier
     * @param certificate
     *            - content of the certificate
     * @return 200OK or exception in case of error
     */
    @POST
    @Path(SAVE_CERTIFICATE_FOR_ELECTION_EVENT)
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response saveCertificate(@PathParam(QUERY_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(QUERY_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @NotNull final InputStream certificate, @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();

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

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

		try (ResponseBody responseBody = RetrofitConsumer
				.processResponse(certificateServiceClient.saveCertificate(CERTIFICATES_DATA_PATH, tenantId,
						electionEventId, xForwardedFor, trackingId, originator, signature, body))) {
			return Response.ok().build();
		} catch (RetrofitException rfE) {
			LOG.error("Error trying to save certificate for election event.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }

    /**
     * Stores a certificate in the registry
     *
     * @param tenantId
     *            - tenant identifier
     * @param certificate
     *            - content of the certificate
     * @return 200OK or exception in case of error
     */
    @POST
    @Path(SAVE_CERTIFICATE_FOR_TENANT)
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response saveCertificate(@PathParam(QUERY_PARAMETER_TENANT_ID) final String tenantId,
            @NotNull final InputStream certificate, @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();

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

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

		try (ResponseBody responseBody = RetrofitConsumer.processResponse(certificateServiceClient.saveCertificate(
				CERTIFICATES_DATA_PATH, tenantId, xForwardedFor, trackingId, originator, signature, body))) {
			return Response.ok().build();
		} catch (RetrofitException rfE) {
			LOG.error("Error trying to save certificate for Tenant.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }

    /**
     * Stores a certificate in the registry
     *
     * @param certificate
     *            - content of the certificate
     * @return 200OK or exception in case of error
     */
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response saveCertificate(@NotNull final InputStream certificate, @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();

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

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

        try {
			RetrofitConsumer.processResponse(certificateServiceClient.saveCertificate(CERTIFICATES_DATA_PATH, xForwardedFor, trackingId, originator,
			    signature, body));
	        return Response.ok().build();
        } catch (RetrofitException rfE) {
			LOG.error("Error trying to save certificate.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }

    /**
     * Searches for a certificate with the given parameters
     *
     * @param certificateName
     *            - certificate name
     * @return an object with the certificate data
     */
    @GET
    @Path(GET_CERTIFICATE)
    @Produces(MediaType.APPLICATION_JSON)
    public Response getCertificate(@PathParam(QUERY_PARAMETER_CERTIFICATE_NAME) final String certificateName,
            @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();
        
        try {
        	JsonObject processResponse = RetrofitConsumer.processResponse(
        			certificateServiceClient.getCertificate(CERTIFICATES_DATA_PATH, certificateName, xForwardedFor, trackingId));
	        return Response.ok().entity(processResponse.toString()).build();
        } catch (RetrofitException rfE) {
			LOG.error("Error trying to get certificate.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }

    /**
     * Searches for a certificate with the given parameters
     *
     * @param tenantId
     *            - tenant identifier
     * @param certificateName
     *            - certificate name
     * @return an object with the certificate data
     */

    @GET
    @Path(GET_CERTIFICATE_FOR_TENANT)
    @Produces(MediaType.APPLICATION_JSON)
    public Response getCertificate(@PathParam(QUERY_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(QUERY_PARAMETER_CERTIFICATE_NAME) final String certificateName,
            @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();

        try {
        	JsonObject processResponse = RetrofitConsumer.processResponse(
        			certificateServiceClient.getCertificate(CERTIFICATES_DATA_PATH, tenantId, certificateName,
        		            xForwardedFor, trackingId));
	        return Response.ok().entity(processResponse.toString()).build();
        } catch (RetrofitException rfE) {
			LOG.error("Error trying to get certificate for tenant.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }

    /**
     * Returns the result of validate if certificate exists for the given parameters
     *
     * @param tenantId
     *            - the tenant identifier.
     * @param name
     *            - the ballot box identifier.
     * @return Returns the result of the validation.
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path(CHECK_IF_CERTIFICATE_EXIST)
    public Response checkIfCertificateExist(@PathParam(QUERY_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(QUERY_PARAMETER_CERTIFICATE_NAME) final String name, @Context final HttpServletRequest request) {
        String xForwardedFor = xForwardedForFactory.newXForwardedFor(request);
        String trackingId = trackIdGenerator.generate();

        try {
        	JsonObject processResponse = RetrofitConsumer.processResponse(
        			certificateServiceClient.checkIfCertificateExists(CERTIFICATES_DATA_PATH, tenantId, name,
        		            xForwardedFor, trackingId));
	        return Response.ok().entity(processResponse.toString()).build();
        } catch (RetrofitException rfE) {
			LOG.error("Error trying to check if certificate exist.", rfE);
			return Response.status(rfE.getHttpCode()).build();
		}
    }
}
