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

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ws.rs.core.MediaType;

import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.slf4j.Logger;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.proofs.ProofsServiceAPI;
import com.scytl.cryptolib.mathematical.groups.impl.Exponent;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;
import com.scytl.cryptolib.proofs.cryptoapi.ProofVerifierAPI;
import com.scytl.cryptolib.proofs.proof.Proof;
import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.ov.commons.beans.domain.model.messaging.PayloadVerificationException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.validation.CryptographicOperationException;
import com.scytl.products.ov.commons.domain.model.platform.PlatformCAEntity;
import com.scytl.products.ov.commons.domain.model.platform.PlatformCARepository;
import com.scytl.products.ov.commons.dto.ChoiceCodesVerificationResPayload;
import com.scytl.products.ov.commons.dto.PartialCodesInput;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;
import com.scytl.products.ov.commons.validation.ValidationResult;
import com.scytl.products.ov.commons.verify.PayloadVerifier;
import com.scytl.products.ov.vv.domain.model.choicecode.CodesComputeResults;
import com.scytl.products.ov.vv.domain.model.content.VerificationContentRepository;
import com.scytl.products.ov.vv.domain.model.verification.VerificationRepository;
import com.scytl.products.ov.vv.domain.service.ValidationService;
import com.scytl.products.ov.vv.infrastructure.remote.OrchestratorClient;

import okhttp3.ResponseBody;
import retrofit2.Call;

@RunWith(MockitoJUnitRunner.class)
public class CodesComputeServiceTest {

    private final static String TEST_ROOT_CA_CERTIFICATE =
        "-----BEGIN CERTIFICATE-----MIIDbzCCAlegAwIBAgIUXbBGcIUMqgpjO1tuS4id9XahygkwDQYJKoZIhvcNAQELBQAwXzEWMBQGA1UEAwwNU2N5dGwgUm9vdCBDQTEWMBQGA1UECwwNT25saW5lIFZvdGluZzEVMBMGA1UECgwMT3JnYW5pemF0aW9uMQkwBwYDVQQHDAAxCzAJBgNVBAYTAkVTMB4XDTE4MDcxMTEyMzMxNVoXDTE5MDcxMTEyMzMxNlowXzEWMBQGA1UEAwwNU2N5dGwgUm9vdCBDQTEWMBQGA1UECwwNT25saW5lIFZvdGluZzEVMBMGA1UECgwMT3JnYW5pemF0aW9uMQkwBwYDVQQHDAAxCzAJBgNVBAYTAkVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi0mRnBYdh+BpA7MpZ1jatMiD6GR/8qT9nN76S3OtA16/Ho148U1GO1A2mOjB+QFPbo8/H3OeluvmQKLpkiYePgk3+XnPyu1jY12AJ4NLCqizi98HuJk9HFEEicWjscn8Xyh1XoiVvtq/lvEKUzzeX1ehDn316YkbXbaz9xtMOMMAs4TaUeEZaa5Vf0UQ00/M54+R/p8yrVkHpm8ffhWGSMAyhVU9DQh9vdgctHfNJ8io4H+5vPAVMYg7/bD76ColRgKq12vHacJKB6LjAleofvwpf7biw1PKrl1hQhMlyy6tIqdYP20MDHz5eE9BRi/E+x9C8qjyKBpDJ1g/fFBQFwIDAQABoyMwITAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAVJs8AJzYG9n6jZRy4pxkiXz4/qEp91GFO1CLnvMcjqdkHot/rJVSmXPMuOrYGCWB12JLWcgmv4ez4JtLoULXrSqt7hqPrGJiCMNqp9UUEBlOzERDywR8vO54P7PWZyDXc5GW8EQDX5A26BJfNoXXDP4ajBGJLEaawyklWk2mLcMZ38F6RK+2gMighdO5QdEZyVqLurSqK/Zhqf1VnS9CO8tkH4JJWQbvPi/TujZKZRr9SVFtMmYzNxubX130AJwj81I2y5umrLHCw+eD31fSud5gLiHWIqbLcaYFethFpAnUbonLsAqvc/0pqU76oeCP+umNUZYyWllzPd0GGH1+MQ==-----END CERTIFICATE-----";

    @Mock
    private Logger LOG;

    @Mock
    private TrackIdInstance trackIdInstance;

    @Mock
    private VerificationContentRepository verificationContentRepository;

    @Mock
    private VerificationRepository verificationRepository;

    @Mock
    private OrchestratorClient ccOrchestratorClient;

    @Mock
    private ProofsServiceAPI proofsService;

    @Mock
    private SecureLoggingWriter loggingWriter;

    @Mock
    private PlatformCARepository platformCARepository;

    @Mock
    private PayloadVerifier payloadVerifier;

    @Mock
    private ValidationService validationService;

    @InjectMocks
    private CodesComputeService codesComputeService = new CodesComputeService();

    private String exponentiatedGamma;

    @Before
    public void setUp()
            throws ResourceNotFoundException, GeneralCryptoLibException, IOException, CryptographicOperationException,
            IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException,
            PayloadVerificationException {

        exponentiatedGamma = IOUtils.toString(this.getClass().getResourceAsStream("/exponentiatedGamma.json"), "UTF-8");

        when(trackIdInstance.getTrackId()).thenReturn("trackId");

        ChoiceCodesVerificationResPayload payload = new ChoiceCodesVerificationResPayload();
        payload.setCastCodeDerivedKeyJson("derivedKeyJson");
        payload.setExponentiationProofJson(new Proof(new Exponent(BigInteger.ONE, BigInteger.ONE),
            Collections.singletonList(new Exponent(BigInteger.ONE, BigInteger.ONE))).toJson());

        Map<BigInteger, BigInteger> primeToComputedPrime = new HashMap<BigInteger, BigInteger>();
        primeToComputedPrime.put(new BigInteger("617"), new BigInteger("953"));
        primeToComputedPrime.put(new BigInteger("9421"), new BigInteger("9739"));
        payload.setPrimeToComputedPrime(primeToComputedPrime);

        List<ChoiceCodesVerificationResPayload> payloadList = Arrays.asList(payload, payload);

        byte[] serializedPayload;

        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            oos.writeObject(payloadList);
            serializedPayload = bos.toByteArray();
        }

        ResponseBody responseBody =
            ResponseBody.create(okhttp3.MediaType.parse(MediaType.APPLICATION_OCTET_STREAM), serializedPayload);

        Call<ResponseBody> callMock = Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(retrofit2.Response.success(responseBody));

        when(ccOrchestratorClient.getChoiceCodeNodesComputeContributions(any(), any(), any(), any(), any(), any(),
            any())).thenReturn(callMock);

        PlatformCAEntity platformCAEntity = new PlatformCAEntity();

        platformCAEntity.setCertificateContent(TEST_ROOT_CA_CERTIFICATE);
        when(platformCARepository.getRootCACertificate()).thenReturn(platformCAEntity);
        when(payloadVerifier.isValid(any(), any())).thenReturn(true);

        ProofVerifierAPI proofVerifier = Mockito.mock(ProofVerifierAPI.class);

        when(proofsService.createProofVerifierAPI(any())).thenReturn(proofVerifier);
        when(proofVerifier.verifyExponentiationProof(any(), any(), any())).thenReturn(true);
        when(validationService.validate(any(), any(), any(), any())).thenReturn(new ValidationResult(true));
    }

    @Test
    public void testComputeCodesSuccessful()
            throws ResourceNotFoundException, CryptographicOperationException, GeneralCryptoLibException,
            NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {

        ZpGroupElement zpGroupElement = ZpGroupElement.fromJson(exponentiatedGamma);

        BigInteger g = new BigInteger("2");
        BigInteger p = zpGroupElement.getP();
        BigInteger q = zpGroupElement.getQ();

        ZpSubgroup group = new ZpSubgroup(g, p, q);

        PartialCodesInput partialCodesInput = new PartialCodesInput();
        partialCodesInput.setPartialCodesElements(Arrays.asList(new BigInteger("617"), new BigInteger("9421")));

        CodesComputeResults result = codesComputeService.computePartialCodes("100",
            "9914226cae8a48c796015dd74f7c0fa3", "123", "123", group, partialCodesInput);

        Assert.assertEquals(new BigInteger("908209"), result.getCombinedPartialChoiceCodes().get(new BigInteger("617")).getValue());
        Assert.assertEquals(new BigInteger("94848121"), result.getCombinedPartialChoiceCodes().get(new BigInteger("9421")).getValue());
    }

}
