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

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;

import com.scytl.cryptolib.api.asymmetric.AsymmetricServiceAPI;
import com.scytl.cryptolib.api.elgamal.ElGamalServiceAPI;
import com.scytl.cryptolib.api.primitives.PrimitivesServiceAPI;
import com.scytl.cryptolib.api.proofs.ProofsServiceAPI;
import com.scytl.cryptolib.mathematical.groups.activity.GroupElementsCompressor;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.products.oscore.logging.api.factory.SecureLoggingFactory;
import com.scytl.products.oscore.logging.api.formatter.MessageFormatter;
import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.oscore.logging.core.factory.SecureLoggingFactoryLog4j;
import com.scytl.products.oscore.logging.core.formatter.SplunkFormatter;
import com.scytl.products.oscore.logging.core.provider.TransactionInfoProvider;
import com.scytl.products.ov.channel.cc.commons.slogger.ServiceTransactionInfoProvider;
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.sign.CryptolibPayloadSigner;
import com.scytl.products.ov.commons.sign.PayloadSigner;
import com.scytl.products.ov.commons.verify.CryptolibPayloadSigningCertificateValidator;
import com.scytl.products.ov.commons.verify.CryptolibPayloadVerifier;
import com.scytl.products.ov.commons.verify.PayloadSigningCertificateValidator;
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.constants.Constants;
import com.scytl.products.ov.mixnet.commons.validation.EncryptedBallotsDuplicationValidator;
import com.scytl.products.ov.mixnet.spring.MixingSecureLoggerConfig;
import com.scytl.products.ov.mixnet.mvc.MixingController;
import com.scytl.products.ov.mixnet.mvc.VerifyingController;

/**
 * Mixnet services configuration.
 */
@Configuration
public class MixingConfig {

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

    @Bean
    public static MixingSecureLoggerConfig mixingSecureLoggerConfig() {
        return new MixingSecureLoggerConfig();
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public ServiceTransactionInfoProvider getServiceTransactionInfoProvider() {
        return new ServiceTransactionInfoProvider();
    }

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

    @Bean
    public VerifyingController verifyingController(final VerifyingServiceAPI verifyingServiceAPI) {
        return new VerifyingController(verifyingServiceAPI);
    }

    @Bean
    public MixingServiceAPI mixingServiceAPI(final MixnetLoadBalancer mixnetLoadBalancer) {
        return new MixingService(mixnetLoadBalancer);
    }

    @Bean
    public VerifyingServiceAPI verifyingServiceAPI(final BGVerifier verifier) {
        return new VerifyingService(verifier);
    }

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

    @Bean
    public SecureLoggingFactory secureLoggingFactory(
            TransactionInfoProvider transactionInfoProvider) {
        MessageFormatter formatter = new SplunkFormatter("OV", "MIX", transactionInfoProvider);
        return new SecureLoggingFactoryLog4j(formatter);
    }

    @Bean
    public SecureLoggingWriter secureLoggingWriter(
            final SecureLoggingFactory secureLoggingFactory) {
        return secureLoggingFactory.getLogger(Constants.SECURE_LOGGER_NAME);
    }

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

    @Bean
    public PayloadSigningCertificateValidator certificateChainValidator() {
        return new CryptolibPayloadSigningCertificateValidator();
    }

    @Bean
    public PayloadSigner payloadSigner(AsymmetricServiceAPI asymmetricService) {
        return new CryptolibPayloadSigner(asymmetricService);
    }

    @Bean
    public PayloadVerifier payloadVerifier(AsymmetricServiceAPI asymmetricService,
            PayloadSigningCertificateValidator certificateChainValidator) {
        return new CryptolibPayloadVerifier(asymmetricService, certificateChainValidator);
    }

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

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

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

    @Bean
    public SecureLoggingHelperFactory secureLoggingHelperFactory(
            SecureLoggingWriter secureLoggingWriter, FingerprintGenerator fingerPrintGenerator,
            @Value("${keys.nodeId}") String nodeName) {
        return new SecureLoggingHelperFactory(secureLoggingWriter, fingerPrintGenerator, nodeName);
    }
}
