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

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonValue;

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

import com.orientechnologies.common.exception.OException;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.iterator.ORecordIteratorClass;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.scytl.products.ov.commons.util.JsonUtils;
import com.scytl.products.ov.sdm.application.exception.DatabaseException;
import com.scytl.products.ov.sdm.domain.model.ballot.BallotRepository;
import com.scytl.products.ov.sdm.infrastructure.DatabaseFixture;
import com.scytl.products.ov.sdm.infrastructure.DatabaseManager;
import com.scytl.products.ov.sdm.infrastructure.JsonConstants;

/**
 * Tests of {@link BallotBoxRepositoryImpl}.
 */
public class BallotBoxRepositoryImplTest {
    private DatabaseFixture fixture;

    private DatabaseManager manager;

    private BallotRepository ballotRepository;

    private BallotBoxRepositoryImpl repository;

    @Before
    public void setUp() throws OException, IOException {
        fixture = new DatabaseFixture(getClass());
        fixture.setUp();
        manager = fixture.databaseManager();
        ballotRepository = mock(BallotRepository.class);
        repository = new BallotBoxRepositoryImpl(manager);
        repository.ballotRepository = ballotRepository;
        repository.initialize();
        URL resource =
            getClass().getResource(getClass().getSimpleName() + ".json");
        fixture.createDocuments(repository.entityName(), resource);
    }

    @After
    public void tearDown() {
        fixture.tearDown();
    }

    @Test
    public void testFindByElectoralAuthority() {
        String json = repository
            .findByElectoralAuthority("331279febbb0423298d44ee58702d581");
        JsonArray array = JsonUtils.getJsonObject(json)
            .getJsonArray(JsonConstants.JSON_ATTRIBUTE_NAME_RESULT);
        assertEquals(2, array.size());
        Set<String> ids = new HashSet<>();
        for (JsonValue value : array) {
            ids.add(((JsonObject) value)
                .getString(JsonConstants.JSON_ATTRIBUTE_NAME_ID));
        }
        assertTrue(
            ids.containsAll(asList("03bbc588d0b04d9bbfe60df81b94943e",
                "eb6ed778b44c463f8b54c95e846dfe49")));
    }

    @Test
    public void testFindByElectoralAuthorityNotFound() {
        String json = repository
            .findByElectoralAuthority("unknownElectoralAuthority");
        JsonArray array = JsonUtils.getJsonObject(json)
            .getJsonArray(JsonConstants.JSON_ATTRIBUTE_NAME_RESULT);
        assertTrue(array.isEmpty());
    }

    @Test
    public void testGetBallotId() {
        String ballotId =
            repository.getBallotId("03bbc588d0b04d9bbfe60df81b94943e");
        assertEquals("6db73a32b0ec4f72b259ed5b70945267", ballotId);
    }

    @Test
    public void testGetBallotIdNotFound() {
        assertTrue(repository.getBallotId("unknownBallotBox").isEmpty());
    }

    @Test
    public void testListAliases() {
        List<String> aliases =
            repository.listAliases("bf411ab6bc76483dbed6dd9684a50592");
        assertEquals(2, aliases.size());
        assertTrue(aliases.containsAll(asList("2", "3")));
    }

    @Test
    public void testListAliasesNotFound() {
        assertTrue(repository.listAliases("unknownBallot").isEmpty());
    }

    @Test
    public void testListByElectionEvent() {
        String json = repository
            .listByElectionEvent("101549c5a4a04c7b88a0cb9be8ab3df6");
        JsonArray array = JsonUtils.getJsonObject(json)
            .getJsonArray(JsonConstants.JSON_ATTRIBUTE_NAME_RESULT);
        assertEquals(2, array.size());
        Set<String> ids = new HashSet<>();
        for (JsonValue value : array) {
            ids.add(((JsonObject) value)
                .getString(JsonConstants.JSON_ATTRIBUTE_NAME_ID));
        }
        assertTrue(
            ids.containsAll(asList("03bbc588d0b04d9bbfe60df81b94943e",
                "eb6ed778b44c463f8b54c95e846dfe49")));
    }

    @Test
    public void testListByElectionEventNotFound() {
        String json =
            repository.listByElectionEvent("unknownElectionEvent");
        JsonArray array = JsonUtils.getJsonObject(json)
            .getJsonArray(JsonConstants.JSON_ATTRIBUTE_NAME_RESULT);
        assertTrue(array.isEmpty());
    }

    @Test
    public void testUpdateRelatedBallotAlias() {
        when(ballotRepository
            .listAliases("6db73a32b0ec4f72b259ed5b70945267"))
                .thenReturn(asList("alias1", "alias2"));
        when(ballotRepository
            .listAliases("bf411ab6bc76483dbed6dd9684a50592"))
                .thenReturn(asList("alias3", "alias4"));
        repository.updateRelatedBallotAlias(
            asList("03bbc588d0b04d9bbfe60df81b94943e",
                "eb6ed778b44c463f8b54c95e846dfe49"));
        try (ODatabaseDocument database = manager.openDatabase()) {
            ORecordIteratorClass<ODocument> iterator =
                database.browseClass(repository.entityName());
            while (iterator.hasNext()) {
                ODocument document = iterator.next();
                String id = document.field(
                    JsonConstants.JSON_ATTRIBUTE_NAME_ID, String.class);
                String alias = document.field("ballotAlias", String.class);
                switch (id) {
                case "03bbc588d0b04d9bbfe60df81b94943e":
                    assertEquals("alias1,alias2", alias);
                    break;
                case "eb6ed778b44c463f8b54c95e846dfe49":
                    assertEquals("alias3,alias4", alias);
                    break;
                default:
                    assertEquals("352", alias);
                }
            }
        }
    }

    @Test(expected = DatabaseException.class)
    public void testUpdateRelatedBallotAliasNotFound() {
        repository
            .updateRelatedBallotAlias(singletonList("unknownBallotBox"));
    }
}
