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

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.bean.ElGamalEncryptionParameters;
import com.scytl.products.oscore.logging.core.provider.TransactionInfoProvider;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.services.MixDecKeyGenerationService;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.services.MixDecKeysResponse;
import com.scytl.products.ov.commons.beans.exceptions.ApplicationException;
import com.scytl.products.ov.commons.beans.exceptions.ApplicationExceptionMessages;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.key.generation.request.KeyGenerationRequestParameters;
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.ErrorCodes;
import com.scytl.products.ov.commons.util.HttpRequestService;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.util.Base64;
import java.util.List;
import java.util.Map;
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.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.Response.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Path(MixDecKeysResource.RESOURCE_PATH)
@Stateless(name = "or-MixDecKeysResource")
public class MixDecKeysResource {

    static final String RESOURCE_PATH = "mixdec/tenant/{tenantId}/electionevent/{electionEventId}/keys";

    static final String PATH_PARAMETER_TENANT_ID = "tenantId";

    static final String PATH_PARAMETER_ELECTION_EVENT_ID = "electionEventId";

    @Inject
    private MixDecKeyGenerationService mixDecKeyGenerationService;

    @Inject
    private TrackIdInstance trackIdInstance;

    @Inject
    private TransactionInfoProvider transactionInfoProvider;

    @Inject
    private HttpRequestService httpRequestService;

    private static final Logger LOG = LoggerFactory.getLogger("std");
    
    /**
     * Provides synchronous generation of MixDec keys.
     */
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response generateKeys(@HeaderParam(Constants.PARAMETER_HEADER_X_REQUEST_ID) String trackingId,
            @PathParam(PATH_PARAMETER_TENANT_ID) final String tenantId,
            @PathParam(PATH_PARAMETER_ELECTION_EVENT_ID) final String electionEventId,
            @NotNull final KeyGenerationRequestParameters keyGenerationRequestParameters,
            @Context final HttpServletRequest request) {
        try {
            trackIdInstance.setTrackId(trackingId);

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

            validateParameters(tenantId, electionEventId);

            List<String> electoralAuthorityIds = keyGenerationRequestParameters.getResourceIds();
            ZonedDateTime keysDateFrom = ZonedDateTime.parse(keyGenerationRequestParameters.getKeysDateFrom());
            ZonedDateTime keysDateTo = ZonedDateTime.parse(keyGenerationRequestParameters.getKeysDateTo());
            ElGamalEncryptionParameters elGamalEncryptionParameters = recoverEncryptionParameters(keyGenerationRequestParameters.getElGamalEncryptionParameters());

            LOG.info("Requesting MixDec key generation for tenant {} electionEventId {}", tenantId, electionEventId);

            Map<String, List<String>> electoralAuthorityKeys = mixDecKeyGenerationService
                    .requestMixDecKeyGenerationSync(trackingId, tenantId, electionEventId, electoralAuthorityIds,
                            keysDateFrom, keysDateTo, elGamalEncryptionParameters);

            MixDecKeysResponse mixDecKeysResponse = new MixDecKeysResponse();
            mixDecKeysResponse.setElectoralAuthorityMixDecryptKeys(electoralAuthorityKeys);

            return Response.ok().entity(mixDecKeysResponse).build();
        } catch (ResourceNotFoundException e) {
            LOG.error("Resource not found", e);
            return Response.status(Status.NOT_FOUND).build();
        } catch (ApplicationException | GeneralCryptoLibException | IOException e) {
            LOG.error("Mixing key generation failed", e);
            return Response.serverError().entity(e).build();
        }
    }

    private ElGamalEncryptionParameters recoverEncryptionParameters(String encodedEncryptionParameters)
            throws GeneralCryptoLibException {

        String encryptionParamsAsJson =
            new String(Base64.getDecoder().decode(encodedEncryptionParameters), StandardCharsets.UTF_8);
        return ElGamalEncryptionParameters.fromJson(encryptionParamsAsJson);
    }

    private void validateParameters(String tenantId, String electionEventId) throws ApplicationException {

        if (tenantId == null || tenantId.isEmpty())
            throw new ApplicationException(ApplicationExceptionMessages.EXCEPTION_MESSAGE_QUERY_PARAMETER_IS_NULL,
                RESOURCE_PATH, ErrorCodes.MISSING_QUERY_PARAMETER, PATH_PARAMETER_TENANT_ID);

        if (electionEventId == null || electionEventId.isEmpty()) {
            throw new ApplicationException(ApplicationExceptionMessages.EXCEPTION_MESSAGE_QUERY_PARAMETER_IS_NULL,
                RESOURCE_PATH, ErrorCodes.MISSING_QUERY_PARAMETER, PATH_PARAMETER_ELECTION_EVENT_ID);
        }
    }
}
