/*
 * 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.vv.ui.ws.rs.application.operation;

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

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ValidationException;
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 javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriBuilderException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import com.scytl.products.oscore.logging.core.provider.TransactionInfoProvider;
import com.scytl.products.ov.commons.beans.exceptions.ApplicationException;
import com.scytl.products.ov.commons.beans.exceptions.DuplicateEntryException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.exceptions.SemanticErrorException;
import com.scytl.products.ov.commons.beans.exceptions.SyntaxErrorException;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;
import com.scytl.products.ov.commons.ui.Constants;
import com.scytl.products.ov.commons.ui.ws.rs.ObjectMappers;
import com.scytl.products.ov.commons.util.HttpRequestService;
import com.scytl.products.ov.commons.util.ValidationUtils;
import com.scytl.products.ov.vv.domain.model.verification.Verification;
import com.scytl.products.ov.vv.domain.model.verification.VerificationRepository;

/**
 * Service which offers the possibility of creating voter verifications in the system.
 */
@Path(VerificationResource.RESOURCE_PATH)
@Stateless
public class VerificationResource {
	
	static final String RESOURCE_PATH = "/verifications";
	
	static final String GET_VOTER_INFORMATION_PATH = "/tenant/{tenantId}/electionevent/{electionEventId}/verificationcard/{verificationCardId}";

    // Path to the created resource.
    private static final String PATH_GET_VOTER_MATERIALS = "verifications/{verificationId}";

    // The name of the parameter value tenant id.
    static final String PARAMETER_VALUE_TENANT_ID = "tenantId";

    // The name of the parameter value verification card id.
    static final String PARAMETER_VALUE_VERIFICATION_CARD_ID = "verificationCardId";

    // String which defines the resource.
    private static final String RESOURCE = "VERIFICATION";

    // The name of the parameter value election event id.
    static final String PARAMETER_VALUE_ELECTION_EVENT_ID = "electionEventId";

    // The constant for the error code "mandantory.field".
    private static final String ERROR_CODE_MANDATORY_FIELD = "mandatory.field";

    // The constant for the message "Verification card id is null".
    private static final String VERIFIATION_CARD_ID_IS_NULL = "Verification card id is null";

    // The constant for the message "Election event id is null".
    private static final String ELECTION_EVENT_ID_IS_NULL = "Election event id is null";

    // The constant for the message "Tenant id is null".
    private static final String TENANT_ID_IS_NULL = "Tenant id is null";

    // An instance of the voter verification repository
    @EJB
    private VerificationRepository voterVerificationRepository;

    private static final Logger LOG = LoggerFactory.getLogger(VerificationResource.class);

    // The track id instance
    @Inject
    private TrackIdInstance tackIdInstance;

    @Inject
    private TransactionInfoProvider transactionInfoProvider;

    @Inject
    private HttpRequestService httpRequestService;

    /**
     * Creates a set of identifiers of voter information.
     *
     * @param voterVerification - the information to be stored.
     * @param trackId           - the track id to be used for logging purposes.
     * @param request           - the http servlet request.
     * @return The http response of execute the operation. HTTP status code 201 if the request has succeed.
     * @throws URISyntaxException       if it is not possible to build the URI of the created resource.
     * @throws DuplicateEntryException  Duplicate entry in database for this resource.
     * @throws UriBuilderException      if it is not possible to build the URI of the created resource.
     * @throws IllegalArgumentException if it is not possible to build the URI of the created resource.
     * @throws SemanticErrorException   Errors during validation of input parameters.
     * @throws SyntaxErrorException     Errors during validation of input parameters.
     */
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createVoterVerification(@NotNull @HeaderParam(Constants.PARAMETER_HEADER_X_REQUEST_ID) String trackingId,
                                            @NotNull Verification voterVerification,
                                            @Context HttpServletRequest request)
        throws URISyntaxException, IOException, ValidationException, IllegalArgumentException,
        UriBuilderException, DuplicateEntryException, SyntaxErrorException, SemanticErrorException {
        // set the track id to be logged
        tackIdInstance.setTrackId(trackingId);

        // transaction id generation
        transactionInfoProvider.generate(voterVerification.getTenantId(), httpRequestService.getIpClientAddress(request),
            httpRequestService.getIpServer(request));

        // validate voter verification
        ValidationUtils.validate(voterVerification);

        LOG.info("Creating the voter verification for tenant: {}, election event: {} and verification card: {}.",
            voterVerification.getTenantId(), voterVerification.getElectionEventId(),
            voterVerification.getVerificationCardId());

        // store the voter verification
        if (voterVerificationRepository.save(voterVerification) != null) {
            UriBuilder uriBuilder = UriBuilder.fromPath(PATH_GET_VOTER_MATERIALS);
            URI uri = uriBuilder.build(voterVerification.getVerificationCardId());

            LOG.info("Voter verification for tenant: {}, election event: {} and verification card: {} created.",
                voterVerification.getTenantId(), voterVerification.getElectionEventId(),
                voterVerification.getVerificationCardId());

            // return location of resource created
            return Response.created(uri).build();
        }
        return Response.noContent().build();
    }

    /**
     * Returns a credential and verification data for a given tenant, election event and verification card.
     *
     * @param trackingId         The track id to be used for logging purposes.
     * @param tenantId           The tenant identifier.
     * @param electionEventId    The election event identifier.
     * @param verificationCardId The verification card identifier.
     * @param request            The http servlet request.
     * @return a credential and verification data.
     * @throws ResourceNotFoundException If voter information is not found.
     * @throws IOException               Conversion exception from object to json. material data. For example, both credential and
     *                                   verification data are null.
     * @throws ApplicationException      if one of the input parameters is null.
     */
    @Path(GET_VOTER_INFORMATION_PATH)
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getVoterInformation(@NotNull @HeaderParam(Constants.PARAMETER_HEADER_X_REQUEST_ID) String trackingId,
                                        @PathParam(PARAMETER_VALUE_TENANT_ID) String tenantId,
                                        @PathParam(PARAMETER_VALUE_ELECTION_EVENT_ID) String electionEventId,
                                        @PathParam(PARAMETER_VALUE_VERIFICATION_CARD_ID) String verificationCardId,
                                        @Context HttpServletRequest request)
        throws IOException, ResourceNotFoundException, ApplicationException {

        // set the track id to be logged
        tackIdInstance.setTrackId(trackingId);

        // transaction id generation
        transactionInfoProvider.generate(tenantId, httpRequestService.getIpClientAddress(request),
            httpRequestService.getIpServer(request));

        // in case something from the input is not valid, it throws an exception
        validateInput(tenantId, electionEventId, verificationCardId);

        Verification verification = voterVerificationRepository.findByTenantIdElectionEventIdVerificationCardId(tenantId,
            electionEventId, verificationCardId);

        String jsonVoterVerification = ObjectMappers.toJson(verification);
        return Response.ok().entity(jsonVoterVerification).build();
    }

    // Validate input parameters.
    private void validateInput(String tenantId, String electionEventId, String verificationCardId)
        throws ApplicationException {
        if (tenantId == null) {
            throw new ApplicationException(TENANT_ID_IS_NULL, RESOURCE, ERROR_CODE_MANDATORY_FIELD,
                PARAMETER_VALUE_TENANT_ID);
        }
        if (electionEventId == null) {
            throw new ApplicationException(ELECTION_EVENT_ID_IS_NULL, RESOURCE, ERROR_CODE_MANDATORY_FIELD,
                PARAMETER_VALUE_ELECTION_EVENT_ID);
        }
        if (verificationCardId == null) {
            throw new ApplicationException(VERIFIATION_CARD_ID_IS_NULL, RESOURCE, ERROR_CODE_MANDATORY_FIELD,
                PARAMETER_VALUE_VERIFICATION_CARD_ID);
        }
    }
}
