/*
 * 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.vm.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.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 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.vm.domain.model.credential.Credential;
import com.scytl.products.ov.vm.domain.model.credential.CredentialDataService;
import com.scytl.products.ov.vm.domain.model.credential.CredentialRepository;
import com.scytl.products.ov.vm.domain.model.information.VoterInformationRepository;

/**
 * Service which offers the possibility of creating credentials in the system.
 */
@Path("/credentials")
@Stateless
public class CredentialResource {

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

    // 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_CREDENTIAL_ID = "credentialId";

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

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

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

    // The constant for the message "Voting card id is null".
    private static final String CREDENTIAL_ID_IS_NULL = "Voting 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";

    private static final Logger LOG = LoggerFactory.getLogger("std");

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

    // An instance of the credential repository
    @EJB
    private CredentialRepository credentialRepository;

    // An instance of the voter information repository
    @EJB
    private VoterInformationRepository voterInformationRepository;

    // An instance of the credential data service
    @Inject
    private CredentialDataService credentialDataService;

    @Inject
    private TransactionInfoProvider transactionInfoProvider;

    @Inject
    private HttpRequestService httpRequestService;

    /**
     * Creates a set of identifiers of voter information.
     *
     * @param voterCredential - the information to be stored.
     * @param trackId         - the track id to be used for logging purposes.
     * @return The http response of execute the operation. HTTP status code 201
     * if the request has succeed.
     * @throws DuplicateEntryException  If voter credential already exists
     * @throws IllegalArgumentException if there location of the resulting created voter information
     *                                  resource or any of its parameters is null.
     * @throws UriBuilderException      if the URI of the resulting created voter information cannot
     *                                  be constructed.
     * @throws SemanticErrorException   if there are semantic errors in json input for voter
     *                                  credential.
     * @throws SyntaxErrorException     if there are syntax errors in json input for voter
     *                                  credential.
     */
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createVoterCredential(@HeaderParam(Constants.PARAMETER_HEADER_X_REQUEST_ID) String trackingId,
                                          @NotNull Credential voterCredential)
        throws IllegalArgumentException, UriBuilderException, DuplicateEntryException, SyntaxErrorException,
        SemanticErrorException {
        // set the track id to be logged
        trackIdInstance.setTrackId(trackingId);

        // validate input parameter
        ValidationUtils.validate(voterCredential);

        LOG.info("Creating the voter credential for tenant: {} and credentialId: {}.", voterCredential.getTenantId(),
            voterCredential.getCredentialId());

        // save the voter credential
        if (credentialRepository.save(voterCredential) != null) {
            // create URI for locating the created resource
            UriBuilder uriBuilder = UriBuilder.fromPath(PATH_GET_VOTER_MATERIALS);
            URI uri = uriBuilder.build(voterCredential.getCredentialId());

            LOG.info("Voter credential for tenant: {} and credentialId: {} created.", voterCredential.getTenantId(),
                voterCredential.getCredentialId());

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

    /**
     * Returns a credential data for a given tenant, election event and voting
     * card.
     *
     * @param tenantId        The tenant identifier.
     * @param electionEventId The election event identifier.
     * @param credentialId    The credential identifier.
     * @param trackId         The track id to be used for logging purposes.
     * @param request         - the http servlet request.
     * @return a credential data.
     * @throws ResourceNotFoundException If voter information is not found.
     * @throws IOException               Conversion exception from object to json.
     * @throws ApplicationException      if one of the input parameters is null.
     */
    @Path("/tenant/{tenantId}/electionevent/{electionEventId}/credential/{credentialId}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getCredential(@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_CREDENTIAL_ID) String credentialId,
                                  @Context HttpServletRequest request)
        throws IOException, ResourceNotFoundException, ApplicationException {

        // set the track id to be logged
        trackIdInstance.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, credentialId);

        // get the credential data base on the tenant, election event and
        // credential id
        Credential credential = credentialDataService.getCredentialData(tenantId, electionEventId, credentialId);

        // convert to json format
        String jsonCredential = ObjectMappers.toJson(credential);
        return Response.ok().entity(jsonCredential).build();
    }

    // Validate input parameters.
    private void validateInput(String tenantId, String electionEventId, String credentialId)
        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 (credentialId == null) {
            throw new ApplicationException(CREDENTIAL_ID_IS_NULL, RESOURCE, ERROR_CODE_MANDATORY_FIELD,
                PARAMETER_VALUE_CREDENTIAL_ID);
        }
    }
}
