/*
 * 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.mixing.service;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

import com.scytl.cryptolib.api.elgamal.ElGamalServiceAPI;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.primitives.PrimitivesServiceAPI;
import com.scytl.cryptolib.api.proofs.ProofsServiceAPI;
import com.scytl.cryptolib.elgamal.bean.ElGamalKeyPair;
import com.scytl.cryptolib.elgamal.service.ElGamalService;
import com.scytl.cryptolib.mathematical.groups.activity.GroupElementsCompressor;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.primitives.service.PrimitivesService;
import com.scytl.cryptolib.proofs.service.ProofsService;
import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.oscore.logging.core.provider.TransactionInfoProvider;
import com.scytl.products.ov.channel.cc.commons.keymanagement.KeyManager;
import com.scytl.products.ov.channel.cc.commons.util.FingerprintGenerator;
import com.scytl.products.ov.channel.cc.mixing.service.conversion.LocationsProviderMixingOutputConverter;
import com.scytl.products.ov.channel.cc.mixing.service.conversion.MixingOutputConverter;
import com.scytl.products.ov.channel.cc.mixing.service.logging.SecureLoggingHelperFactory;
import com.scytl.products.ov.commons.beans.domain.model.messaging.PayloadSignature;
import com.scytl.products.ov.commons.beans.domain.model.messaging.PayloadSignatureException;
import com.scytl.products.ov.commons.beans.domain.model.messaging.PayloadVerificationException;
import com.scytl.products.ov.commons.sign.CryptoTestData;
import com.scytl.products.ov.commons.sign.PayloadSigner;
import com.scytl.products.ov.commons.verify.PayloadVerifier;
import com.scytl.products.ov.mixnet.BGMixnetLoadBalancer;
import com.scytl.products.ov.mixnet.BGVerifier;
import com.scytl.products.ov.mixnet.BallotDecryptionService;
import com.scytl.products.ov.mixnet.MixnetLoadBalancer;
import com.scytl.products.ov.mixnet.PartialBallotDecryptionService;
import com.scytl.products.ov.mixnet.api.MixingService;
import com.scytl.products.ov.mixnet.api.MixingServiceAPI;
import com.scytl.products.ov.mixnet.api.VerifyingService;
import com.scytl.products.ov.mixnet.api.VerifyingServiceAPI;
import com.scytl.products.ov.mixnet.commons.validation.EncryptedBallotsDuplicationValidator;
import com.scytl.products.ov.mixnet.mvc.MixingController;
import com.scytl.products.ov.mixnet.mvc.VerifyingController;

@Configuration
@PropertySource("classpath:/application.properties")
class TestConfig {

    static final String ELECTORAL_AUTHORITY_ID = "electoralAuthorityId";

    static final String BALLOT_BOX_ID = "ballotBoxId";

    static final String ELECTION_EVENT_ID = "electionEventId";

    static final String TENANT_ID_VALUE = "100";

    static final String BALLOT_JSON = "input/ballot.json";

    static final int VOTESET_INDEX = 0;

    static final String REQUEST_ID = "requestId";

    static final byte[] SIGNATURE_CONTENTS = "signatureContents".getBytes(StandardCharsets.UTF_8);

    @Bean
    ElGamalKeyPair electoralAuthorityKeyPair() throws GeneralCryptoLibException {
        return CryptoTestData.generateElGamalKeyPair(1);
    }

    @Bean
    GroupElementsCompressor<ZpGroupElement> groupElementsCompressor() {
        return new GroupElementsCompressor<>();
    }

    @Bean
    PropertySourcesPlaceholderConfigurer propertiesResolver() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    InternalMixDecryptService service() {
        return new InternalMixDecryptServiceImpl();
    }

    @Bean
    MixingPropertiesProvider mixingPropertiesProvider() {
        return new MixingPropertiesProviderImpl();
    }

    @Bean
    public FingerprintGenerator getFingerprintGenerator(PrimitivesServiceAPI primitivesService) {
        return new FingerprintGenerator(primitivesService);
    }

    @Bean
    KeyManager keyManager(ElGamalKeyPair electoralAuthorityKeyPair) throws KeyManagementException {
        KeyManager keyManager = mock(KeyManager.class);
        when(keyManager.getMixingPublicKey(anyString(), anyString()))
                .thenReturn(electoralAuthorityKeyPair.getPublicKeys());
        when(keyManager.getMixingPrivateKey(anyString(), anyString()))
                .thenReturn(electoralAuthorityKeyPair.getPrivateKeys());

        return keyManager;
    }

    @Bean
    KeyRepository keyRepository() {
        return new KeyRepositoryImpl();
    }

    @Bean
    MixingController mixingController(MixingServiceAPI mixingService,
            TransactionInfoProvider transactionInfoProvider) {
        return new MixingController(mixingService, transactionInfoProvider);
    }

    @Bean
    MixingServiceAPI mixingService(MixnetLoadBalancer mixnetLoadBalancer) {
        return new MixingService(mixnetLoadBalancer);
    }

    @Bean
    public BallotDecryptionService ballotDecryptionService(PrimitivesServiceAPI primitivesService,
            ElGamalServiceAPI elGamalService, ProofsServiceAPI proofsService,
            TransactionInfoProvider transactionInfoProvider, SecureLoggingWriter loggingWriter)
            throws GeneralCryptoLibException {
        return new PartialBallotDecryptionService(transactionInfoProvider, primitivesService,
                elGamalService, proofsService, loggingWriter);
    }

    @Bean
    MixnetLoadBalancer balancer(PrimitivesServiceAPI primitivesService,
            BallotDecryptionService ballotDecryptionService,
            TransactionInfoProvider transactionInfoProvider, SecureLoggingWriter loggingWriter)
            throws GeneralCryptoLibException {
        return new BGMixnetLoadBalancer.Builder().setPrimitivesService(primitivesService)
                .setBallotDecryptionService(ballotDecryptionService)
                .setTransactionInfoProvider(transactionInfoProvider).setLoggingWriter(loggingWriter)
                .addBallotsValidator(new EncryptedBallotsDuplicationValidator()).build();
    }

    @Bean
    PrimitivesServiceAPI primitivesService() throws GeneralCryptoLibException {
        return new PrimitivesService();
    }

    @Bean
    ElGamalServiceAPI elGamalService() throws GeneralCryptoLibException {
        return new ElGamalService();
    }

    @Bean
    ProofsServiceAPI proofsService() throws GeneralCryptoLibException {
        return new ProofsService();
    }

    @Bean
    SecureLoggingWriter secureLogger() {
        SecureLoggingWriter secureLogger = mock(SecureLoggingWriter.class);
        doNothing().when(secureLogger).log(any(), any(), any());
        doNothing().when(secureLogger).log(any(), any());

        return secureLogger;
    }

    @Bean
    VerifyingController verifyingController(VerifyingServiceAPI verifyingService) {
        return new VerifyingController(verifyingService);
    }

    @Bean
    VerifyingServiceAPI verifyingService(BGVerifier bgVerifier) {
        return new VerifyingService(bgVerifier);
    }

    @Bean
    BGVerifier bgVerifier(SecureLoggingWriter loggingWriter) {
        return new BGVerifier(loggingWriter);
    }

    @Bean
    TransactionInfoProvider transactionInfoProvider() {
        return new TransactionInfoProvider();
    }

    @Bean
    PayloadVerifier payloadVerifier() throws PayloadVerificationException {
        PayloadVerifier payloadVerifier = mock(PayloadVerifier.class);
        when(payloadVerifier.isValid(any(), any())).thenReturn(true);

        return payloadVerifier;
    }

    @Bean
    PayloadSigner payloadSigner() throws PayloadSignatureException {
        PayloadSigner payloadSigner = mock(PayloadSigner.class);
        PayloadSignature mockSignature = mock(PayloadSignature.class);
        when(mockSignature.getSignatureContents()).thenReturn(SIGNATURE_CONTENTS);
        when(payloadSigner.sign(any(), any(), any())).thenReturn(mockSignature);

        return payloadSigner;
    }

    @Bean
    public MixingOutputConverter mixingOutputConverter() {
        return new LocationsProviderMixingOutputConverter();
    }

    @Bean
    public SecureLoggingHelperFactory secureLoggingHelperFactory() {
        SecureLoggingHelperFactory secureLoggingHelperFactory =
                mock(SecureLoggingHelperFactory.class);
        when(secureLoggingHelperFactory.create(anyString(), any()))
                .thenReturn(mock(SecureLoggingHelper.class));

        return secureLoggingHelperFactory;
    }
}
