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

import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.jbehave.core.model.ExamplesTable;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import org.xml.sax.SAXException;

import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.scytl.product.ov.sdm.plugin.PhaseName;
import com.scytl.product.ov.sdm.plugin.Plugins;
import com.scytl.products.ov.sdm.plugin.ExecutionListenerImpl;
import com.scytl.products.ov.sdm.plugin.Parameters;
import com.scytl.products.ov.sdm.plugin.PluginSequenceResolver;
import com.scytl.products.ov.sdm.plugin.SequentialExecutor;
import com.scytl.products.ov.sdm.plugin.SequentialExecutorImpl;
import com.scytl.products.ov.sdm.plugin.XmlObjectsLoader;

public class Steps {

    private String xmlLocation;

    private PluginSequenceResolver pluginSequenceResolver;

    private List<String> actionsForPhase;

    List<String> commandsForPhase;

    private Parameters parameters;
    ExecutionListenerImpl listener = new ExecutionListenerImpl();
    Plugins plugins;

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

    private SequentialExecutor sequentialExecutor = new SequentialExecutorImpl();;


    /***************************************************************
     *                                                             *
     *                         XML LOAD                            *
     *                                                             *
     ***************************************************************/
    @Given("a valid xml file $location")
    public void locateXml(final String location) throws Exception {
        xmlLocation = resolveAbsolutePathOfResource(location);
    }

    @When("loading the file")
    public void loadXml() throws Exception {
        pluginSequenceResolver = new PluginSequenceResolver(
                XmlObjectsLoader.unmarshal(Paths.get(xmlLocation)));
    }

    @When("filtering for '$phaseName'")
    public void filterPhase(String phaseName) throws Exception {
        actionsForPhase = pluginSequenceResolver.getActionsForPhase(phaseName);
    }

    @Then("the xml should have the following content: $expectedResult")
    public void checkCommands(final ExamplesTable expectedResult)
            throws Exception {
        List<String> commands = getExpectedResult(expectedResult);
        for (int i = 0; i < 4; i++) {
            Assert.assertEquals(commands.get(i), actionsForPhase.get(i));
        }

    }

    @Then("the xml can not be parsed")
    public void xmlUnparseable() throws IOException, JAXBException, SAXException {
        exception.expect(RuntimeException.class);
        Plugins plugins = XmlObjectsLoader.unmarshal(Paths.get(xmlLocation));
        pluginSequenceResolver = new PluginSequenceResolver(plugins);
    }

    @Then("the xml can not be loaded")
    public void xmlNotFound(final ExamplesTable expectedResult)
            throws Exception {
        List<String> commands = getExpectedResult(expectedResult);
        for (int i = 0; i < 4; i++) {
            Assert.assertEquals(commands.get(i), actionsForPhase.get(i));
        }

    }

    private List<String> getExpectedResult(final ExamplesTable table) {
        List<String> result = new ArrayList<String>();
        for (Map<String, String> row : table.getRows()) {
            String command = row.get("command");
            result.add(command);
        }
        return result;
    }

    private String resolveAbsolutePathOfResource(String string) {
        try {
            return Paths.get(this.getClass().getResource(string).toURI())
                .toFile().getAbsolutePath();
        } catch (URISyntaxException e) {
            throw new RuntimeException(
                "Resource at " + string + " cannot be found", e);
        }
    }

    /***************************************************************
     *                                                             *
     *                  COMMAND INVOKE ERROR                       *
     *                                                             *
     ***************************************************************/

    @Given("a path of xml file with the commands to invoke $location")
    public void givenXmlPath(final String location) throws Exception {
        if(XmlObjectsLoader.validatePath(resolveAbsolutePathOfResource(location))){
            xmlLocation = resolveAbsolutePathOfResource(location);
        }
    }

    @When("xml ummarshal is correct")
    public void whenUnmarshallOk() throws Exception {
        pluginSequenceResolver = new PluginSequenceResolver(
                XmlObjectsLoader.unmarshal(Paths.get(xmlLocation)));
        Assert.assertNotNull(pluginSequenceResolver);
    }

    @When("command is invoke ok")
    public void whenCommandInvokeOk() throws Exception {

    }

    @Then("the ouput of test: $testCase should be: $expectedResult" )
    public void commandsErrorBy(final Integer testCase, final String expectedResult) throws Exception {
        List<String> commandsForPhase =
            pluginSequenceResolver.getActionsForPhaseAndOrder(PhaseName.GENERATE_PRE_VOTING_OUTPUTS.value(), testCase);
        invoke(expectedResult, commandsForPhase);

    }
    private void invoke(String expectedResult, List<String> commandsForPhase) throws URISyntaxException {
        if(!System.getProperty("os.name").toLowerCase().contains("win")){
            return;
        }
        commandsForPhase = sanitize(commandsForPhase, expectedResult);
        sequentialExecutor.execute(commandsForPhase, parameters, listener);
        Assert.assertEquals(expectedResult, Integer.toString(listener.getError()));
    }
    private List<String> sanitize(List<String> commandsForPhase, String errorCode) throws URISyntaxException {
        List<String> sanitized= new ArrayList<>();
        for (String string : commandsForPhase) {
            sanitized.add(sanitize(string, errorCode));
        }
        return sanitized;
    }

    private String sanitize(String string, String errorCode) throws URISyntaxException {
        String absolutePath = Paths.get(this.getClass().getResource("/error"+errorCode+".bat").toURI()).toFile().getAbsolutePath();
        return absolutePath;
    }


    /***************************************************************
     *                                                             *
     *                  WRONG PARAMETER PASSED                     *
     *                     COPYTO/COPYFROM                         *
     *                                                             *
     ***************************************************************/

    @When("obtain params of copyTo command")
    public void whenObtainParamsCopyTo(final String location) throws Exception {
        pluginSequenceResolver = new PluginSequenceResolver(
                XmlObjectsLoader.unmarshal(Paths.get(xmlLocation)));
    }

    @When("obtain params of copyFrom command")
    public void whenObtainParamsCopyFrom(final String location) throws Exception {
//        xmlPathLocation = checkPathOfResource(location);
    }

    @Then("the validation not passed by letter")
    public void valitacionNokByLetter(final ExamplesTable expectedResult)
            throws Exception {
        List<String> commands = getExpectedResult(expectedResult);
        for (int i = 0; i < 4; i++) {
            Assert.assertEquals(commands.get(i), actionsForPhase.get(i));
        }
    }

    @Then("the validation not passed by ee_alias")
    public void valitacionNokByEealias(final ExamplesTable expectedResult)
            throws Exception {
        List<String> commands = getExpectedResult(expectedResult);
        for (int i = 0; i < 4; i++) {
            Assert.assertEquals(commands.get(i), actionsForPhase.get(i));
        }
    }

    @Then("the validation not passed by ee_id")
    public void valitacionNokByEeid(final ExamplesTable expectedResult)
            throws Exception {
        List<String> commands = getExpectedResult(expectedResult);
        for (int i = 0; i < 4; i++) {
            Assert.assertEquals(commands.get(i), actionsForPhase.get(i));
        }
    }

    @Then("command terminate unexpectly")
    public void commandsUnexpectly(final ExamplesTable expectedResult)
            throws Exception {
        List<String> commands = getExpectedResult(expectedResult);
        for (int i = 0; i < 4; i++) {
            Assert.assertEquals(commands.get(i), actionsForPhase.get(i));
        }
    }

    /***************************************************************
     *                                                             *
     *                  WRONG PARAMETER PASSED                     *
     *                     FILECONVERTER                           *
     *                                                             *
     ***************************************************************/

    @When("obtain fileConverter params")
    public void whenObtainParamsFileConverter(final String location) throws Exception {
//        xmlPathLocation = checkPathOfResource(location);
    }

    @Then("all params are setted")
    public void valitacionAllParamsRight(final ExamplesTable expectedResult)
            throws Exception {
        List<String> commands = getExpectedResult(expectedResult);
        for (int i = 0; i < 4; i++) {
            Assert.assertEquals(commands.get(i), actionsForPhase.get(i));
        }
    }

    @Then("missing parameters")
    public void valitacionMissingParams(final ExamplesTable expectedResult)
            throws Exception {
        List<String> commands = getExpectedResult(expectedResult);
        for (int i = 0; i < 4; i++) {
            Assert.assertEquals(commands.get(i), actionsForPhase.get(i));
        }
    }
}
