/*
 * 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.channel.cc.orchestrator.ui.ws.rs.operation;

import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import org.jboss.arquillian.extension.rest.client.ArquillianResteasyResource;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;

import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.model.MixDecBallotBoxStatus;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.model.MixDecNodeOutput;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.model.MixDecNodeOutputId;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.services.PayloadJson;
import com.scytl.products.ov.commons.beans.domain.model.ballotbox.BallotBoxId;
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.dto.mixing.MixingPayloadMockBuilder;

@RunWith(Arquillian.class)
public class MixDecBallotBoxResourceArquillianTest {

    private static final String TRACK_ID_HEADER = "X-Request-ID";

    private static final String TEST_TRACK_ID = "TestTrackingId";

    private static final List<String> NODE_NAMES = Arrays.asList("m1", "m2", "m3");

    private static final int NUM_VOTE_SETS = 10;

    private static String payloadJson;

    private List<String> payloadJsonFileNames = new ArrayList<>();

    @PersistenceContext(unitName = "persistenceUnitJdbc")
    EntityManager entityManager;

    @Resource
    UserTransaction userTransaction;

    @BeforeClass
    public static void loadPayloadJsonTemplate() throws IOException, URISyntaxException {
        // Load payload JSON
        payloadJson = new String(Files.readAllBytes(
            Paths.get(Thread.currentThread().getContextClassLoader().getResource("payload.json").toURI())));
    }

    @Before
    public void prepareDatabase() throws Exception {
        cleanDatabase();
        populateDatabase();
    }

    @Before
    public void cleanDatabase()
            throws NotSupportedException, SystemException, SecurityException, IllegalStateException, RollbackException,
            HeuristicMixedException, HeuristicRollbackException {
        userTransaction.begin();
        Query query = entityManager.createQuery("DELETE FROM " + MixDecNodeOutput.class.getSimpleName());
        query.executeUpdate();
        userTransaction.commit();

        payloadJsonFileNames.clear();
    }

    @Test
    public void testGetNodeOutputs(@ArquillianResteasyResource("") ResteasyWebTarget webTarget) throws IOException {
        String nodeOutputsPath = UriBuilder.fromPath(MixDecBallotBoxResource.RESOURCE_PATH)
            .segment(MixDecBallotBoxResource.PATH_NODE_OUTPUTS).build(MixingPayloadMockBuilder.TENANT_ID,
                MixingPayloadMockBuilder.ELECTION_EVENT_ID, MixingPayloadMockBuilder.BALLOT_BOX_ID)
            .toString();

        // Request the payload JSONs. The response should be a zip with the
        // expected set of JSON files in it.
        InputStream is = webTarget.path(nodeOutputsPath).request(MediaType.APPLICATION_OCTET_STREAM)
            .header(TRACK_ID_HEADER, TEST_TRACK_ID).get(InputStream.class);

        ZipInputStream zip = new ZipInputStream(is);

        ZipEntry zipEntry;
        while (null != (zipEntry = zip.getNextEntry())) {
            assertTrue(zipEntry.getName() + " is not known", payloadJsonFileNames.contains(zipEntry.getName()));
        }
    }

    /**
     * Place a few ballot box statuses in the database.
     */
    private void populateDatabase() throws SystemException {
        try {
            userTransaction.begin();

            // Store the payloads
            BallotBoxId bbid = new BallotBoxIdImpl(MixingPayloadMockBuilder.TENANT_ID,
                MixingPayloadMockBuilder.ELECTION_EVENT_ID, MixingPayloadMockBuilder.BALLOT_BOX_ID);
            // For each vote set
            for (int i = 0; i < NUM_VOTE_SETS; i++) {
                final int voteSetIndex = i;
                VoteSetId voteSetId = new VoteSetIdImpl(bbid, voteSetIndex);
                // For each node
                NODE_NAMES.stream().forEach(nodeName -> {
                    // Create a node output entity
                    MixDecNodeOutputId nodeOutputId = new MixDecNodeOutputId(voteSetId, nodeName);
                    MixDecNodeOutput entity = new MixDecNodeOutput(nodeOutputId, payloadJson);
                    payloadJsonFileNames.add(new PayloadJson(bbid, nodeName, voteSetIndex, "").getFileName());
                    // Store it.
                    entityManager.persist(entity);
                });
            }

            // Set the status of the ballot box to 'mixed'
            MixDecBallotBoxStatus status = new MixDecBallotBoxStatus();
            status.setTenantId(bbid.getTenantId());
            status.setElectionEventId(bbid.getElectionEventId());
            status.setBallotBoxId(bbid.getId());
            status.setStatus("MIXED");
            entityManager.persist(status);
            
            userTransaction.commit();
        } catch (NotSupportedException | SystemException | HeuristicMixedException | HeuristicRollbackException |

                RollbackException e) {
            userTransaction.rollback();
        }
    }
}
