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

import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyVararg;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Scanner;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Qualifier;

import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.oscore.logging.core.provider.TransactionInfoProvider;
import com.scytl.products.ov.commons.beans.domain.model.ballotbox.BallotBoxIdImpl;
import com.scytl.products.ov.commons.beans.domain.model.vote.VoteSetId;
import com.scytl.products.ov.commons.beans.domain.model.vote.VoteSetIdImpl;
import com.scytl.products.ov.commons.path.PathResolver;
import com.scytl.products.ov.sdm.domain.model.votingcardset.VotingCardSetRepository;
import com.scytl.products.ov.sdm.domain.service.ConfigConstants;
import com.scytl.products.ov.validation.services.ValidationServiceImpl;

@RunWith(MockitoJUnitRunner.class)
public class ExportImportServiceTest {

    public static final String USB_DRIVE = "usbDrive";

    public static final String TENANT_ID = "tenantId";

    public static final String EE_ID = "eeid";

    public static final String BALLOT_BOX_ID = "ballotBoxId";

    public static final String ALIAS = "alias";

    public static final String TALLY = "tally";

    public static final String EXPORT = "export";

    public static final String MIXED = "mixed";

    public static final String CLEANSED = "cleansed";

    public static final String DOWNLOADED = "downloaded";

    public static final String TALLIED = "tallied";

    public static final String DECRYPTED = "decrypted";

    public static final String VOTING_CARD_SETS_FOLDER_NAME = "vcs";

    private final VoteSetId voteSetId = new VoteSetIdImpl(new BallotBoxIdImpl(TENANT_ID, EE_ID, BALLOT_BOX_ID), 0);

    private final String mixingPayloadJson = String.format("%s-control_component_id.json", voteSetId.toString());

    private final String decryptedBallotsCSV =
        ValidationServiceImpl.getDecompressedVotesFilePath(Paths.get("")).getFileName().toString();

    @Mock
    private PathResolver pathResolver;

    @Mock
    private SecureLoggingWriter secureLoggingWriter;

    @Mock
    private TransactionInfoProvider transactionInfoProvider;

    @Mock
    @Qualifier("absolutePath")
    private PathResolver absolutePathResolver;

    @Mock
    private HashService hashService;

    @Mock
    private VotingCardSetRepository votingCardSetRepository;

    @InjectMocks
    @Spy
    ExportImportService exportImportService = new ExportImportService();

    private String votingCardSetJson;

    @Test
    public void exportTalliedBallotBox() throws URISyntaxException, IOException {

        Path export = getPathOfFileInResources(EXPORT);
        Path source = export.resolve(TALLIED);
        final Path usbDrive = Files.createTempDirectory(USB_DRIVE);
        when(pathResolver.resolve(ConfigConstants.SDM_DIR_NAME)).thenReturn(source);
        when(absolutePathResolver.resolve(eq(USB_DRIVE), anyString())).thenReturn(usbDrive);
        doCallRealMethod().when(exportImportService).exportElectionEventElectionInformation(anyString(), anyString(),
            anyString());
        exportImportService.exportElectionEventElectionInformation(USB_DRIVE, EE_ID, ALIAS);
        Assert.assertFalse(
            checkIfExists(ConfigConstants.CONFIG_FILE_NAME_ELECTION_INFORMATION_DOWNLOADED_BALLOT_BOX, usbDrive));
        Assert.assertFalse(
            checkIfExists(ConfigConstants.CONFIG_FILE_NAME_ELECTION_INFORMATION_ENCRYPTED_BALLOT_BOX, usbDrive));

    }

    @Test
    public void exportDecryptedBallotBox() throws URISyntaxException, IOException {

        Path export = getPathOfFileInResources(EXPORT);
        Path source = export.resolve(DECRYPTED);
        final Path usbDrive = Files.createTempDirectory(USB_DRIVE);
        when(pathResolver.resolve(ConfigConstants.SDM_DIR_NAME)).thenReturn(source);

        when(absolutePathResolver.resolve(eq(USB_DRIVE), anyString())).thenReturn(usbDrive);

        doCallRealMethod().when(exportImportService).exportElectionEventElectionInformation(anyString(), anyString(),
            anyString());
        exportImportService.exportElectionEventElectionInformation(USB_DRIVE, EE_ID, ALIAS);
        Assert.assertFalse(checkIfExists(decryptedBallotsCSV, usbDrive));
        Assert.assertFalse(checkIfExists(ConfigConstants.CONFIG_FILE_NAME_AUDITABLE_VOTES, usbDrive));

    }

    @Test
    public void exportDownloadedBallotBox() throws URISyntaxException, IOException {

        Path export = getPathOfFileInResources(EXPORT);
        Path source = export.resolve(DOWNLOADED);
        final Path usbDrive = Files.createTempDirectory(USB_DRIVE);
        when(pathResolver.resolve(ConfigConstants.SDM_DIR_NAME)).thenReturn(source);

        when(absolutePathResolver.resolve(eq(USB_DRIVE), anyString())).thenReturn(usbDrive);

        doCallRealMethod().when(exportImportService).exportElectionEventElectionInformation(anyString(), anyString(),
            anyString());
        exportImportService.exportElectionEventElectionInformation(USB_DRIVE, EE_ID, ALIAS);

        Assert.assertTrue(
            checkIfExists(ConfigConstants.CONFIG_FILE_NAME_ELECTION_INFORMATION_DOWNLOADED_BALLOT_BOX, usbDrive));
        Assert.assertTrue(checkIfExists(ConfigConstants.CONFIG_FILE_NAME_SUCCESSFUL_VOTES, usbDrive));
        Assert.assertTrue(checkIfExists(ConfigConstants.CONFIG_FILE_NAME_FAILED_VOTES, usbDrive));
        Assert.assertTrue(checkIfExists(mixingPayloadJson, usbDrive));

    }

    @Test
    public void exportPreComputedChoiceCodes() throws URISyntaxException, IOException {
        Path export = getPathOfFileInResources(EXPORT);
        Path source = export.resolve(VOTING_CARD_SETS_FOLDER_NAME);
        when(pathResolver.resolve(anyVararg())).thenReturn(source);

        final Path usbDrive = Files.createTempDirectory(USB_DRIVE);
        when(absolutePathResolver.resolve(anyVararg())).thenReturn(usbDrive);

        when(votingCardSetRepository.listByElectionEvent(anyString())).thenReturn(getVotingCardSetJson());
        doCallRealMethod().when(exportImportService).exportPreComputedChoiceCodes(anyString(), anyString(),
            anyString());

        exportImportService.exportPreComputedChoiceCodes(USB_DRIVE, EE_ID, ALIAS);

        Assert.assertTrue(checkIfExists(ConfigConstants.CONFIG_FILE_NAME_PREFIX_CHOICE_CODE_GENERATION_REQUEST_PAYLOAD
            + 0 + ConfigConstants.CONFIG_FILE_NAME_SUFFIX_CHOICE_CODE_GENERATION_REQUEST_PAYLOAD, usbDrive));

        Assert.assertFalse(checkIfExists(ConfigConstants.CONFIG_VERIFICATION_CARDS_KEY_PAIR_DIRECTORY, usbDrive));
        Assert.assertFalse(
            checkIfExists(ConfigConstants.CONFIG_VERIFICATION_CARDS_KEY_PAIR_DIRECTORY + ConfigConstants.CSV, usbDrive));
    }

    private boolean checkIfExists(String fileName, Path source) throws IOException {

        return Files.walk(source, 20).filter(path -> path.getFileName().toString().equals(fileName)).findAny()
            .isPresent();
    }

    private static Path getPathOfFileInResources(final String path) throws URISyntaxException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        URL resource = classLoader.getResource(path);
        return Paths.get(resource.toURI());
    }

    /**
     * Returns a sample voting card set JSON string.
     * 
     * @return a sample voting card set as a JSON string
     * @throws IOException
     */
    private String getVotingCardSetJson() throws IOException {
        if (null == votingCardSetJson) {
            try (InputStream is = getClass().getResourceAsStream("/votingCardSet.json");
                    Scanner scanner = new Scanner(is, "UTF-8");) {
                votingCardSetJson = scanner.useDelimiter("\\A").next();
            }
        }

        return votingCardSetJson;
    }
}
