/*
 * 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
 */
/**
* @author lsantamaria
* @date 11/04/18
* <p>
* Copyright (C) 2017 Scytl Secure Electronic Voting SA
* <p>
* All rights reserved.
*/

package com.scytl.products.ov.sdm.application.service;

import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.ws.rs.core.MediaType;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.ov.commons.path.PathResolver;
import com.scytl.products.ov.sdm.domain.model.ballotbox.BallotBoxRepository;
import com.scytl.products.ov.sdm.domain.model.status.Status;
import com.scytl.products.ov.sdm.domain.service.ConfigConstants;
import com.scytl.products.ov.sdm.infrastructure.clients.ElectionInformationClient;
import com.scytl.products.ov.sdm.infrastructure.service.ConfigurationEntityStatusService;

import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;

@RunWith(MockitoJUnitRunner.class)
public class CleansingOutputsDownloadServiceTest {

    @Rule
    public ExpectedException exception = ExpectedException.none();

    @Mock
    private BallotBoxRepository ballotBoxRepository;

    @Mock
    private ConfigurationEntityStatusService configurationEntityStatusService;

    @Mock
    private PathResolver pathResolver;

    @Mock
    private SecureLoggingWriter secureLoggingWriter;

    @InjectMocks
    @Spy
    private CleansingOutputsDownloadService cleansingOutputsDownloadService = new CleansingOutputsDownloadService();

    private final String ballotBoxId = "7674245a6382449fb1afedcce1810565";

    private final String ballotId = "76c70f14cd424cff89a3bfc6d1b6e51c";

    private final String ballotBoxJsonElectionMixed = "{\"result\": [ { \"id\":\"" + ballotBoxId
        + "\", \"ballot\":{ \"id\":\"76c70f14cd424cff89a3bfc6d1b6e51c\"} ,\"status\":\"" + Status.MIXED
        + "\", \"test\":\"" + Boolean.FALSE + "\", \"synchronized\":\"" + Boolean.TRUE + "\"} ]}";

    private final String electionEventId = "1";

    @Mock
    private ElectionInformationClient clientMock;

    private final String ballotBoxCsv = "10,10;100;100";

    @Mock
    private Path pathMock;

    @Before
    public void injectValueAnnotatedFields() {
        ReflectionTestUtils.setField(cleansingOutputsDownloadService, "tenantId", "tenant");
    }

    @Test
    public void whenDownloadSuccessfulVoteAndResponseNotOKFromVPThenException() throws Exception {
        ElectionInformationClient clientMock = Mockito.mock(ElectionInformationClient.class);

        when(ballotBoxRepository.list(any())).thenReturn(ballotBoxJsonElectionMixed);
        doReturn(clientMock).when(cleansingOutputsDownloadService).getElectionInformationClient();
        @SuppressWarnings("unchecked")
        Call<ResponseBody> callMock = Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.error(500,
            ResponseBody.create(okhttp3.MediaType.parse(MediaType.APPLICATION_OCTET_STREAM), ballotBoxCsv)));
        doReturn(callMock).when(clientMock).downloadSuccessfulVotes(anyString(), anyString(), anyString());
        exception.expect(CleansingOutputsDownloadException.class);
        exception.expectMessage(String.format("Failed to download successful votes [electionEvent=%s, ballotBox=%s]",
            electionEventId, ballotBoxId));

        cleansingOutputsDownloadService.download(electionEventId, ballotBoxId);
    }

    @Test
    public void whenDownloadSuccessfulVotesThenOK()
            throws IOException, BallotBoxDownloadException, GeneralCryptoLibException,
            CleansingOutputsDownloadException {
        ElectionInformationClient clientMock = Mockito.mock(ElectionInformationClient.class);
        Path successfulVotesPath =
            Files.createTempDirectory("temp").resolve(ConfigConstants.CONFIG_FILE_NAME_SUCCESSFUL_VOTES);

        when(ballotBoxRepository.list(any())).thenReturn(ballotBoxJsonElectionMixed);
        doReturn(clientMock).when(cleansingOutputsDownloadService).getElectionInformationClient();
        @SuppressWarnings("unchecked")
        Call<ResponseBody> callMock = Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response
            .success(ResponseBody.create(okhttp3.MediaType.parse(MediaType.APPLICATION_OCTET_STREAM), ballotBoxCsv)));
        doReturn(callMock).when(clientMock).downloadSuccessfulVotes(anyString(), anyString(), anyString());
        doReturn(callMock).when(clientMock).downloadFailedVotes(anyString(), anyString(), anyString());
        when(pathResolver.resolve(Matchers.<String> anyVararg())).thenReturn(successfulVotesPath);

        cleansingOutputsDownloadService.download(electionEventId, ballotBoxId);

        assertTrue(Files.exists(successfulVotesPath));
    }

    @Test
    public void whenWritingSuccessfulVotesToWrongPathThenException()
            throws IOException, CleansingOutputsDownloadException {
        Path wrongPath = Paths.get("/wrongpath/successfulVotes.csv");

        when(ballotBoxRepository.list(any())).thenReturn(ballotBoxJsonElectionMixed);
        doReturn(clientMock).when(cleansingOutputsDownloadService).getElectionInformationClient();
        @SuppressWarnings("unchecked")
        Call<ResponseBody> callMock = Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response
            .success(ResponseBody.create(okhttp3.MediaType.parse(MediaType.APPLICATION_OCTET_STREAM), ballotBoxCsv)));
        doReturn(callMock).when(clientMock).downloadSuccessfulVotes(anyString(), anyString(), anyString());
        doReturn(callMock).when(clientMock).downloadFailedVotes(anyString(), anyString(), anyString());
        when(pathResolver.resolve(Matchers.<String> anyVararg())).thenReturn(wrongPath);
        exception.expect(CleansingOutputsDownloadException.class);
        exception.expectMessage(String.format("Failed to write successful votes [electionEvent=%s, ballotBox=%s]",
            electionEventId, ballotBoxId));

        cleansingOutputsDownloadService.download(electionEventId, ballotBoxId);
    }

    @Test
    public void whenWritingFailedVotesToWrongPathThenException() throws IOException, CleansingOutputsDownloadException {
        Path failedVotesWrongPath = Paths.get("/wrongpath/successfulVotes.csv");
        Path successfulVotesCorrectPAth =
            Files.createTempDirectory("temp").resolve(ConfigConstants.CONFIG_FILE_NAME_SUCCESSFUL_VOTES);

        when(ballotBoxRepository.list(any())).thenReturn(ballotBoxJsonElectionMixed);
        doReturn(clientMock).when(cleansingOutputsDownloadService).getElectionInformationClient();
        @SuppressWarnings("unchecked")
        Call<ResponseBody> callMock = Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response
            .success(ResponseBody.create(okhttp3.MediaType.parse(MediaType.APPLICATION_OCTET_STREAM), ballotBoxCsv)));
        doReturn(callMock).when(clientMock).downloadSuccessfulVotes(anyString(), anyString(), anyString());
        doReturn(callMock).when(clientMock).downloadFailedVotes(anyString(), anyString(), anyString());
        when(pathResolver.resolve(ConfigConstants.CONFIG_FILES_BASE_DIR, electionEventId,
            ConfigConstants.CONFIG_DIR_NAME_ONLINE, ConfigConstants.CONFIG_DIR_NAME_ELECTIONINFORMATION,
            ConfigConstants.CONFIG_DIR_NAME_BALLOTS, ballotId, ConfigConstants.CONFIG_DIR_NAME_BALLOTBOXES, ballotBoxId,
            ConfigConstants.CONFIG_FILE_NAME_SUCCESSFUL_VOTES)).thenReturn(successfulVotesCorrectPAth);
        when(pathResolver.resolve(ConfigConstants.CONFIG_FILES_BASE_DIR, electionEventId,
            ConfigConstants.CONFIG_DIR_NAME_ONLINE, ConfigConstants.CONFIG_DIR_NAME_ELECTIONINFORMATION,
            ConfigConstants.CONFIG_DIR_NAME_BALLOTS, ballotId, ConfigConstants.CONFIG_DIR_NAME_BALLOTBOXES, ballotBoxId,
            ConfigConstants.CONFIG_FILE_NAME_FAILED_VOTES)).thenReturn(failedVotesWrongPath);
        exception.expect(CleansingOutputsDownloadException.class);
        exception.expectMessage(String.format("Failed to write failed votes [electionEvent=%s, ballotBox=%s]",
            electionEventId, ballotBoxId));

        cleansingOutputsDownloadService.download(electionEventId, ballotBoxId);
    }
}
