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

import com.google.gson.Gson;

import java.io.IOException;
import javax.ejb.Stateless;
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.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.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.dto.CastCodeAndComputeResults;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;
import com.scytl.products.ov.commons.ui.Constants;
import com.scytl.products.ov.commons.util.HttpRequestService;
import com.scytl.products.ov.ei.services.domain.model.castcode.VoteCastCode;
import com.scytl.products.ov.ei.services.domain.model.castcode.VoteCastCodeRepository;
import com.scytl.products.ov.ei.services.domain.model.validation.ValidationResult;
import com.scytl.products.ov.ei.services.domain.service.vote.VoteCastCodeService;

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

/**
 * The endpoint for cast codes.
 */
@Path(CastCodeResource.RESOURCE_PATH)
@Stateless(name = "ei-CastCodeResource")
public class CastCodeResource {

    static final String RESOURCE_PATH = "/castcodes";

    static final String STORE_CAST_CODE_PATH =
        "/tenant/{tenantId}/electionevent/{electionEventId}/votingcard/{votingCardId}";

    static final String GET_CAST_CODE_PATH =
        "/tenant/{tenantId}/electionevent/{electionEventId}/votingcard/{votingCardId}";

    static final String CHECK_CAST_CODE_PATH =
        "/tenant/{tenantId}/electionevent/{electionEventId}/votingcard/{votingCardId}/available";

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

    // The name of the parameter value voting card id.
    private static final String PARAMETER_VALUE_VOTING_CARD_ID = "votingCardId";

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

    private final Gson gson = new Gson();

    @Inject
    private VoteCastCodeRepository voteCastCodeRepository;

    @Inject
    private TrackIdInstance trackIdInstance;

    @Inject
    private TransactionInfoProvider transactionInfoProvider;

    @Inject
    private HttpRequestService httpRequestService;
    
    @Inject
    private VoteCastCodeService voteCastCodeService;

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

    /**
     * Stores the vote cast codes in the repository.
     *
     * @param trackingId
     *            - the track id to be used for logging purposes.
     * @param tenantId
     *            - Tenant identifier
     * @param electionEventId
     *            - election event identifier
     * @param votingCardId
     *            - voting card identifier
     * @param voteCastCode
     *            - the vote cast code.
     * @param request
     *            - the http servlet request.
     * @return the result of the validation
     * @throws SemanticErrorException
     *             if there are semantic errors in any parameter.
     * @throws SyntaxErrorException
     *             if there are syntax errors in any parameter.
     * @throws DuplicateEntryException
     *             if the object exists for the given tenant, election event and
     *             voting card.
     */
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @Path(STORE_CAST_CODE_PATH)
    public Response storeCastCode(@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_VOTING_CARD_ID) String votingCardId, @NotNull CastCodeAndComputeResults voteCastCode,
            @Context HttpServletRequest request) throws SyntaxErrorException, SemanticErrorException, IOException {
        // set the track id to be logged
        trackIdInstance.setTrackId(trackingId);

        // transaction id generation
        transactionInfoProvider.generate(tenantId, httpRequestService.getIpClientAddress(request),
            httpRequestService.getIpServer(request));
        
        ValidationResult result = new ValidationResult();
        result.setResult(true);
        
        try {
            voteCastCodeService.save(tenantId, electionEventId, votingCardId, voteCastCode);    
        } catch (ApplicationException | IOException e) {
            LOG.error("Error saving vote cast code", e);
            result.setResult(false);
        }

        // convert to string
        String json = gson.toJson(result);

        return Response.ok(json).build();
    }

    /**
     * Retrieve the vote cast code.
     *
     * @param tenantId
     *            - Tenant identifier
     * @param electionEventId
     *            - election event identifier
     * @param votingCardId
     *            - voting card identifier
     * @param trackingId
     *            - the track id to be used for logging purposes.
     * @param request
     *            - the http servlet request.
     * @return the result of the validation
     * @throws ResourceNotFoundException
     *             if the vote cast code data can not be found.
     */
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path(GET_CAST_CODE_PATH)
    public Response getVoteCastCode(@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_VOTING_CARD_ID) String votingCardId, @Context HttpServletRequest request)
            throws ResourceNotFoundException {
        // set the track id to be logged
        trackIdInstance.setTrackId(trackingId);

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

        // get the cast code from the repository.
        VoteCastCode voteCastCode =
            voteCastCodeRepository.findByTenantIdElectionEventIdVotingCardId(tenantId, electionEventId, votingCardId);

        // returns the vote cast code
        return Response.ok(voteCastCode).build();
    }

    @GET
    @Path(CHECK_CAST_CODE_PATH)
    public Response checkVoteCastCode(@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_VOTING_CARD_ID) String votingCardId, @Context HttpServletRequest request) {
        try {
            getVoteCastCode(trackingId, tenantId, electionEventId, votingCardId, request);
            // no exception, we found it. ignore result.
            return Response.ok().build();
        } catch (ResourceNotFoundException e) {
        	LOG.info("Resource not found checking the vote cast code.", e);
            return Response.status(Response.Status.NOT_FOUND).build();
        }
    }

}
