/*
 * 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 java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Properties;

import javax.json.JsonObject;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.scytl.products.ov.commons.path.PathResolver;
import com.scytl.products.ov.commons.ui.ws.rs.ObjectMappers;
import com.scytl.products.ov.commons.util.JsonUtils;
import com.scytl.products.ov.datapacks.beans.CertificateParameters.Type;
import com.scytl.products.ov.sdm.domain.model.ballotbox.BallotBoxRepository;
import com.scytl.products.ov.sdm.domain.model.config.CreateBallotBoxesInput;
import com.scytl.products.ov.sdm.domain.model.electionevent.ElectionEventRepository;
import com.scytl.products.ov.sdm.domain.model.generator.DataGeneratorResponse;
import com.scytl.products.ov.sdm.domain.service.BallotBoxDataGeneratorService;
import com.scytl.products.ov.sdm.domain.service.ConfigConstants;
import com.scytl.products.ov.sdm.domain.service.utils.SystemTenantPublicKeyLoader;
import com.scytl.products.ov.sdm.infrastructure.JsonConstants;

/**
 * This implementation contains a call to the configuration engine for generating the ballot box data.
 */
@Service
public class BallotBoxDataGeneratorServiceImpl implements BallotBoxDataGeneratorService {

    private static final Logger LOGGER = LoggerFactory.getLogger(BallotBoxDataGeneratorServiceImpl.class);

    @Autowired
    private BallotBoxRepository ballotBoxRepository;

    @Autowired
    private ElectionEventRepository electionEventRepository;

    @Autowired
    private PathResolver pathResolver;

    @Autowired
    private SystemTenantPublicKeyLoader systemTenantPublicKeyLoader;

    @Value("${tenantID}")
    private String tenantId;

    @Value("${CREATE_BALLOT_BOX_URL}")
    private String createBAllotBoxURL;

    @Value("${ballotbox.certificate.properties}")
    private String ballotBoxCertificateProperties;

    /**
     * The method prepares an input to call the configuration engine like it is done from command line. The input
     * consists of data from the ballot in json format and paths that we use.
     */
    @Override
    public DataGeneratorResponse generate(final String ballotBoxId, final String electionEventId) throws IOException {
        // some basic validation of the input
        DataGeneratorResponse result = new DataGeneratorResponse();
        if (StringUtils.isEmpty(ballotBoxId)) {
            result.setSuccessful(false);
            return result;
        }

        String ballotBoxAsJson = ballotBoxRepository.find(ballotBoxId);
        // simple check if exists data
        if (JsonConstants.JSON_EMPTY_OBJECT.equals(ballotBoxAsJson)) {
            result.setSuccessful(false);
            return result;
        }

        JsonObject ballotBox = JsonUtils.getJsonObject(ballotBoxAsJson);
        String ballotId = ballotBox.getJsonObject(JsonConstants.JSON_ATTRIBUTE_NAME_BALLOT)
            .getString(JsonConstants.JSON_ATTRIBUTE_NAME_ID);

        // The election event is retrieved in order to check later some settings
        String electionEventAsJson = electionEventRepository.find(electionEventId);
        if (JsonConstants.JSON_EMPTY_OBJECT.equals(electionEventAsJson)) {
            result.setSuccessful(false);
            return result;
        }
        JsonObject electionEvent = JsonUtils.getJsonObject(electionEventAsJson);

        Path configPath = pathResolver.resolve(ConfigConstants.CONFIG_FILES_BASE_DIR);
        Path configElectionEventPath = configPath.resolve(electionEventId);

        CreateBallotBoxesInput input = new CreateBallotBoxesInput();
        input.setBallotBoxID(ballotBoxId);
        input.setBallotID(ballotId);
        input.setAlias(ballotBox.getString(JsonConstants.JSON_ATTRIBUTE_NAME_ALIAS));
        input.setElectoralAuthorityID(ballotBox.getJsonObject(JsonConstants.JSON_ATTRIBUTE_NAME_ELECTORAL_AUTHORITY)
            .getString(JsonConstants.JSON_ATTRIBUTE_NAME_ID));
        input.setOutputFolder(configElectionEventPath.toString());
        input.setKeyForProtectingKeystorePassword(getPublicKeyForProtectingKeystorePassword());
        input.setTest(ballotBox.getString(JsonConstants.JSON_ATTRIBUTE_NAME_TEST));
        input.setGracePeriod(ballotBox.getString(JsonConstants.JSON_ATTRIBUTE_NAME_BALLOT_BOX_GRACE_PERIOD));
        input.setConfirmationRequired(ballotBox.getString(JsonConstants.JSON_ATTRIBUTE_NAME_CONFIRMATION_REQUIRED));
        input.setBallotBoxCertificateProperties(getCertificateProperties());

        LOGGER.debug("------------------------------ Dates in BB");
        input.setStart(ballotBox.getString(JsonConstants.JSON_ATTRIBUTE_NAME_DATE_FROM));
        input.setEnd(ballotBox.getString(JsonConstants.JSON_ATTRIBUTE_NAME_DATE_TO));
        input.setValidityPeriod(electionEvent.getJsonObject(JsonConstants.JSON_ATTRIBUTE_NAME_SETTINGS)
            .getInt(JsonConstants.JSON_ATTRIBUTE_CERTIFICATES_VALIDITY));
        input.setWriteInAlphabet(electionEvent.getJsonObject(JsonConstants.JSON_ATTRIBUTE_NAME_SETTINGS)
            .getString(JsonConstants.JSON_PARAM_NAME_WRITE_IN_ALPHABET));
        LOGGER.debug("Start " + input.getStart());
        LOGGER.debug("End " + input.getEnd());
        LOGGER.debug("------------------------------ End Dates in BB");

        // call the configuration engine with the prepared parameters
        try {

            WebTarget ballotBoxClient = createWebClient();

            ballotBoxClient.request().post(Entity.entity(ObjectMappers.toJson(input), MediaType.APPLICATION_JSON));

        } catch (ProcessingException e) {
            LOGGER.error("Error performing post request", e);
            result.setSuccessful(false);
        }

        return result;
    }

    /**
     * Generates a WebTarget client
     *
     * @return
     */
    public WebTarget createWebClient() {
        return ClientBuilder.newClient().target(createBAllotBoxURL);
    }

    private Properties getCertificateProperties() throws IOException {

        LOGGER.info("Obtaining certificate properties from the following paths:");
        LOGGER.info(" " + ballotBoxCertificateProperties);

        Properties loadedBallotBoxCertificateProperties = getCertificateParameters(ballotBoxCertificateProperties);
        LOGGER.info("Obtained certificate properties");

        return loadedBallotBoxCertificateProperties;
    }

    private Properties getCertificateParameters(String path) throws IOException {

        final Properties props = new Properties();
        props.load(new FileInputStream(path));
        return props;
    }
    
    private String getPublicKeyForProtectingKeystorePassword() throws IOException {

        return systemTenantPublicKeyLoader.load(tenantId, "EI", Type.ENCRYPTION);
    }
}
