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

import static com.scytl.products.oscore.logging.api.domain.Level.INFO;

import java.io.IOException;

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.scytl.products.ov.commons.beans.domain.model.receipt.Receipt;
import org.slf4j.Logger;

import com.scytl.products.oscore.logging.api.domain.LogContent;
import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
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.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.exceptions.ValidationException;
import com.scytl.products.ov.commons.beans.exceptions.VoteRepositoryException;
import com.scytl.products.ov.commons.beans.validation.ValidationErrorType;
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.commons.validation.ValidationError;
import com.scytl.products.ov.commons.validation.ValidationResult;
import com.scytl.products.ov.vw.infrastructure.log.SecureLoggerInformation;
import com.scytl.products.ov.vw.infrastructure.log.VotingWorkflowLogConstants;
import com.scytl.products.ov.vw.infrastructure.log.VotingWorkflowLogEvents;
import com.scytl.products.ov.vw.services.domain.model.authentication.AuthenticationToken;
import com.scytl.products.ov.vw.services.domain.model.authentication.AuthenticationTokenService;

import com.scytl.products.ov.vw.services.domain.service.VoteService;

@Stateless(name = "vw-receiptResource")
@Path("/receipts")
public class ReceiptResource {

    @Inject
    private TrackIdInstance trackIdInstance;

    @Inject
    private TransactionInfoProvider transactionInfoProvider;

    @Inject
    private HttpRequestService httpRequestService;

    @Inject
    private VoteService voteService;

    @Inject
    private AuthenticationTokenService authenticationTokenService;

    /**
     * The secure logger writer.
     */
    @Inject
    private SecureLoggingWriter secureLoggerWriter;

    /**
     * The secure logger information.
     */
    @Inject
    private SecureLoggerInformation secureLoggerInformation;

    /**
     * The log.
     */
    @Inject
    private Logger LOG;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/tenant/{tenantId}/electionevent/{electionEventId}/votingcard/{votingCardId}")
    public Response getReceiptByVotingCardId(@HeaderParam(Constants.PARAMETER_HEADER_X_REQUEST_ID) String trackingId,
            @HeaderParam(Constants.PARAMETER_VALUE_AUTHENTICATION_TOKEN) String authenticationTokenString,
            @PathParam(Constants.PARAMETER_VALUE_TENANT_ID) String tenantId,
            @PathParam(Constants.PARAMETER_VALUE_ELECTION_EVENT_ID) String electionEventId,
            @PathParam(Constants.PARAMETER_VALUE_VOTING_CARD_ID) String votingCardId,
            @Context HttpServletRequest request) throws ApplicationException, ResourceNotFoundException, IOException {

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

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

        AuthenticationToken authenticationToken;
        ValidationResult authTokenValidationResult;
        try {
            // convert from json to object
            authenticationToken = ObjectMappers.fromJson(authenticationTokenString, AuthenticationToken.class);

            // validate auth token
            ValidationUtils.validate(authenticationToken);
            // returns the result of authentication token validation
            authTokenValidationResult = authenticationTokenService.validateAuthenticationToken(tenantId,
                electionEventId, votingCardId, authenticationTokenString);
        } catch (IOException | IllegalArgumentException | ValidationException e) {
            final String error = "Invalid authentication token format.";
            LOG.error(error, e);

            ValidationResult validationResult = new ValidationResult(false);
            ValidationError validationError = new ValidationError(ValidationErrorType.FAILED);
            // we could add the error message from the Exception but it may
            // 'leak' too much information
            validationError.setErrorArgs(new String[] {error });
            validationResult.setValidationError(validationError);
            return Response.status(Response.Status.UNAUTHORIZED).entity(validationError).build();
        }

        // data to be logged
        secureLoggerInformation.setAuthenticationToken(authenticationTokenString);
        secureLoggerInformation.setAuthenticationTokenObject(authenticationToken);

        // no need to continue if auth token is invalid
        if (!authTokenValidationResult.isResult()) {
            secureLoggerWriter.log(INFO,
                new LogContent.LogContentBuilder().logEvent(VotingWorkflowLogEvents.STOP_VOTING_PROCESS)
                    .objectId(secureLoggerInformation.getAuthenticationTokenHash()).user(votingCardId)
                    .electionEvent(electionEventId)
                    .additionalInfo(VotingWorkflowLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_ERR_DESC, "Error validating authentication token")
                    .createLogInfo());
            return getResponseInvalidToken(authTokenValidationResult);
        }

        Receipt receipt;
		try {
			receipt = voteService.obtainReceipt(tenantId, electionEventId, votingCardId);
		} catch (VoteRepositoryException e) {
			 LOG.warn("Error trying to obtain receipt.", e);
			 return getResponseInvalidToken(authTokenValidationResult);
		}

        return Response.ok().entity(receipt).build();

    }

    /**
     * Constructs the response given a invalid validation result
     *
     * @param validationResult
     * @return
     */
    private Response getResponseInvalidToken(ValidationResult validationResult) throws ApplicationException {
        try {
            String json = ObjectMappers.toJson(validationResult);
            return Response.status(Response.Status.UNAUTHORIZED).entity(json).build();
        } catch (IOException e) {
            throw new ApplicationException("Failed to convert object to json format.", e);
        }
    }

}
