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

import static java.nio.file.Files.createDirectories;
import static java.nio.file.Files.createFile;
import static java.nio.file.Files.createTempDirectory;
import static java.nio.file.Files.deleteIfExists;
import static java.nio.file.Files.exists;
import static java.nio.file.Files.getLastModifiedTime;
import static java.nio.file.Files.isDirectory;
import static java.nio.file.Files.newDirectoryStream;
import static java.nio.file.Files.readAllBytes;
import static java.nio.file.Files.write;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.scytl.products.ov.commons.path.PathResolver;
import com.scytl.products.ov.sdm.domain.model.ballot.BallotRepository;
import com.scytl.products.ov.sdm.domain.model.generator.DataGeneratorResponse;
import com.scytl.products.ov.sdm.domain.service.ConfigConstants;
import com.scytl.products.ov.sdm.infrastructure.JsonConstants;

/**
 * Tests of {@link BallotBoxDataGeneratorServiceImpl}.
 */
public class BallotDataGeneratorServiceImplTest {
    private static final String JSON = "{\"ballot\": \"Test Ballot\"}";

    private static final String ID = "id";

    private static final String ELECTION_EVENT_ID = "electionEventId";

    private BallotRepository repository;

    private Path folder;

    private Path file;

    private PathResolver resolver;

    private BallotDataGeneratorServiceImpl service;

    private static void deleteRecursively(final Path file)
            throws IOException {
        if (isDirectory(file)) {
            try (DirectoryStream<Path> children =
                newDirectoryStream(file)) {
                for (Path child : children) {
                    deleteRecursively(child);
                }
            }
        }
        deleteIfExists(file);
    }

    @Before
    public void setUp() throws IOException {
        repository = mock(BallotRepository.class);
        when(repository.find(ID)).thenReturn(JSON);
        folder = createTempDirectory("user");
        file = folder.resolve(ELECTION_EVENT_ID)
            .resolve(ConfigConstants.CONFIG_DIR_NAME_ONLINE)
            .resolve(ConfigConstants.CONFIG_DIR_NAME_ELECTIONINFORMATION)
            .resolve(ConfigConstants.CONFIG_DIR_NAME_BALLOTS).resolve(ID)
            .resolve(ConfigConstants.CONFIG_FILE_NAME_BALLOT_JSON);
        resolver = mock(PathResolver.class);
        when(resolver.resolve(ConfigConstants.CONFIG_FILES_BASE_DIR))
            .thenReturn(folder);
        service = new BallotDataGeneratorServiceImpl(repository, resolver);
    }

    @After
    public void tearDown() throws IOException {
        deleteRecursively(folder);
    }

    @Test
    public void testGenerate() throws IOException {
        DataGeneratorResponse response =
            service.generate(ID, ELECTION_EVENT_ID);
        assertTrue(response.isSuccessful());
        assertArrayEquals(JSON.getBytes(StandardCharsets.UTF_8),
            readAllBytes(file));
    }

    @Test
    public void testGenerateBallotNotFound() {
        when(repository.find(ID))
            .thenReturn(JsonConstants.JSON_EMPTY_OBJECT);
        DataGeneratorResponse response =
            service.generate(ID, ELECTION_EVENT_ID);
        assertFalse(response.isSuccessful());
        assertFalse(exists(file));
    }

    @Test
    public void testGenerateFailedToCreateFolder() throws IOException {
        createFile(folder.resolve(ELECTION_EVENT_ID));
        DataGeneratorResponse response =
            service.generate(ID, ELECTION_EVENT_ID);
        assertFalse(response.isSuccessful());
    }

    @Test
    public void testGenerateFileAlreadyExists()
            throws IOException, InterruptedException {
        createDirectories(file.getParent());
        write(file, JSON.getBytes(StandardCharsets.UTF_8));
        FileTime time = getLastModifiedTime(file);
        Thread.sleep(1000);
        DataGeneratorResponse response =
            service.generate(ID, ELECTION_EVENT_ID);
        assertTrue(response.isSuccessful());
        assertEquals(time, getLastModifiedTime(file));
    }

    @Test
    public void testGenerateFileAlreadyExistsDifferent()
            throws IOException {
        createDirectories(file.getParent());
        write(file,
            "Something different".getBytes(StandardCharsets.UTF_8));
        DataGeneratorResponse response =
            service.generate(ID, ELECTION_EVENT_ID);
        assertTrue(response.isSuccessful());
        assertArrayEquals(JSON.getBytes(StandardCharsets.UTF_8),
            readAllBytes(file));
    }

    @Test
    public void testGenerateIOException() throws IOException {
        createDirectories(file);
        DataGeneratorResponse response =
            service.generate(ID, ELECTION_EVENT_ID);
        assertFalse(response.isSuccessful());
    }
}
