/*
 * Copyright (c) eHealth
 */
package be.ehealth.businessconnector.test.genericasync.integration;

import be.cin.mycarenet.esb.common.v2.CommonInput;
import be.cin.mycarenet.esb.common.v2.OrigineType;
import be.cin.nip.async.generic.Confirm;
import be.cin.nip.async.generic.ConfirmResponse;
import be.cin.nip.async.generic.GetResponse;
import be.cin.nip.async.generic.MsgQuery;
import be.cin.nip.async.generic.MsgResponse;
import be.cin.nip.async.generic.Post;
import be.cin.nip.async.generic.PostResponse;
import be.cin.nip.async.generic.Query;
import be.cin.nip.async.generic.TAckResponse;
import be.ehealth.business.mycarenetdomaincommons.builders.CommonBuilder;
import be.ehealth.business.mycarenetdomaincommons.builders.RequestBuilderFactory;
import be.ehealth.business.mycarenetdomaincommons.domain.Blob;
import be.ehealth.business.mycarenetdomaincommons.domain.McnPackageInfo;
import be.ehealth.business.mycarenetdomaincommons.mapper.DomainBlobMapper;
import be.ehealth.business.mycarenetdomaincommons.util.McnConfigUtil;
import be.ehealth.business.mycarenetdomaincommons.util.WsAddressingUtil;
import be.ehealth.businessconnector.genericasync.builders.BuilderFactory;
import be.ehealth.businessconnector.genericasync.builders.ResponseObjectBuilder;
import be.ehealth.businessconnector.genericasync.exception.GenAsyncBusinessConnectorException;
import be.ehealth.businessconnector.genericasync.mappers.CommonInputMapper;
import be.ehealth.businessconnector.genericasync.session.GenAsyncService;
import be.ehealth.businessconnector.genericasync.session.GenAsyncSessionServiceFactory;
import be.ehealth.businessconnector.test.genericasync.test.util.TestUtil;
import be.ehealth.technicalconnector.exception.ConnectorException;
import be.ehealth.technicalconnector.exception.SessionManagementException;
import be.ehealth.technicalconnector.exception.TechnicalConnectorException;
import be.ehealth.technicalconnector.handler.domain.WsAddressingHeader;
import be.ehealth.technicalconnector.idgenerator.IdGeneratorFactory;
import be.ehealth.technicalconnector.utils.ConnectorIOUtils;
import org.joda.time.DateTime;
import org.junit.Assert;
import org.mapstruct.factory.Mappers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import static be.ehealth.technicalconnector.utils.ConnectorXmlUtils.toByteArray;


/**
 * tests the retrieval of a consultation list.
 *
 * @author EHP
 */
public final class GenAsyncIntegrationTestUtil {

    private GenAsyncIntegrationTestUtil() {
        super();
    }

    private static final Logger LOG = LoggerFactory.getLogger(GenAsyncIntegrationTest.class);

    public static void basePostTest(PostParameter postParameter) throws Exception {


        /*
         * MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] hash = expected; md.update(hash); blob.setHashValue(md.digest());
         */

        post(postParameter);

        McnPackageInfo packageInfo = McnConfigUtil.retrievePackageInfo(postParameter.serviceName);
        CommonBuilder commonBuilder = RequestBuilderFactory.getCommonBuilder(postParameter.serviceName);
        OrigineType origin = Mappers.getMapper(CommonInputMapper.class).map(commonBuilder.createOrigin(packageInfo));

        Thread.sleep(5000);
        GetResponse getResponse = get(origin, postParameter.serviceName);

        Assert.assertTrue("there should at least be a return for msg or tack, now both lists are empty", getResponse.getReturn().getMsgCount() > 0 || getResponse.getReturn().getTAckCount() > 0);
        confirm(getResponse, origin, postParameter.serviceName);
    }

    public static void confirm(GetResponse getResponse, OrigineType origin, String serviceName) throws Exception {
        confirm(origin, serviceName, null, getResponse.getReturn().getTAckResponses(), getResponse.getReturn().getMsgResponses());
    }


    public static void confirm(OrigineType origin, String serviceName, Integer oaNumber, List<TAckResponse> tackResponses, List<MsgResponse> messageResponses) throws Exception {
        LOG.error("Creation of the confirm");
        StringBuilder sb = new StringBuilder();
        for (MsgResponse msgResponse : messageResponses) {
            String msg = "confirming message with inputReference " + msgResponse.getCommonOutput().getInputReference();
            LOG.error(msg);
            sb.append("\n").append(msg);
            writeToFile(("confirming message with inputReference " + msgResponse.getCommonOutput().getInputReference()).getBytes(), "confirmedBusinessMessage" + msgResponse.getCommonOutput().getInputReference());
        }
        for (TAckResponse tAckResponse : tackResponses) {
            LOG.error("confirming tack " + tAckResponse.getTAck().getId() + " with result " + tAckResponse.getTAck().getResultMajor());
            sb.append("\n tack : with Xades : ").append(tAckResponse.getXadesT() == null ? "no Xades" : "xades");
            TestUtil.addTackToSb(sb, tAckResponse.getTAck());
        }
        writeToFile(sb.toString().getBytes(), "confirmedMessages" + new DateTime().toString("yyyyMMddhhmmss"));
        GenAsyncService service = GenAsyncSessionServiceFactory.getGenAsyncService(serviceName);
        Confirm confirmRequst = BuilderFactory.getRequestObjectBuilder(serviceName).buildConfirmRequest(origin, messageResponses, tackResponses);
        writeToFile(toByteArray(confirmRequst), "confirmRequest");

        String oa = "";
        if (oaNumber != null) {
            oa = oaNumber.toString();
        }
        ConfirmResponse responseConfirm = service.confirmRequest(confirmRequst, WsAddressingUtil.createHeader(oa, "urn:be:cin:nip:async:generic:confirm:hash"));
        writeToFile(toByteArray(responseConfirm), "responseConfirm");

    }

    public static List<TAckResponse> selectTacksWithStatus(List<TAckResponse> tAckResponses, String status) {
        ArrayList<TAckResponse> result = new ArrayList<TAckResponse>();
        for (TAckResponse tAckResponse : tAckResponses) {
            if (status.equals(tAckResponse.getTAck().getResultMajor())) {
                result.add(tAckResponse);
            }
        }
        return result;
    }

    /**
     * retrieves asynchronous messages that may be waiting.
     *
     * @param origin {@link OrigineType} the originator of the this get request
     * @param serviceName the servicename , used to retrieve config properties from config file
     * @param messageNames the types of messages , if no messageNames are given all messages are returned ex : "GMD-CONSULT-HCP",
     *        "GMD-CLOSURE", "GMD-EXTENSION"
     * @return {@link GetResponse}
     * @throws Exception
     */
    public static GetResponse get(OrigineType origin, String serviceName, String... messageNames) throws Exception {
        return get(origin, serviceName, 100, messageNames);
    }

    /**
     * @param origin
     * @param serviceName
     * @param numberToGet
     * @param messageNames
     * @return
     * @throws URISyntaxException
     * @throws TechnicalConnectorException
     * @throws ConnectorException
     * @throws GenAsyncBusinessConnectorException
     * @throws SessionManagementException
     * @throws InstantiationException
     * @throws IOException
     */
    public static GetResponse get(OrigineType origin, String serviceName, int numberToGet, String... messageNames) throws Exception {
        LOG.debug("Creation of the get");
        MsgQuery msgQuery = new MsgQuery();
        msgQuery.setInclude(true);
        msgQuery.setMax(numberToGet);
        // if no messages are added , all messages are returned
        msgQuery.getMessageNames().clear();
        if (messageNames != null) {
            for (String messageName : messageNames) {
                msgQuery.getMessageNames().add(messageName);
            }
        }

        Query tackQuery = new Query();
        tackQuery.setInclude(true);
        tackQuery.setMax(numberToGet);

        LOG.debug("Send of the get request");
        WsAddressingHeader responseGetHeader = new WsAddressingHeader(new URI("urn:be:cin:nip:async:generic:get:query"));
        responseGetHeader.setMessageID(new URI(IdGeneratorFactory.getIdGenerator("uuid").generateId()));
        GenAsyncService service = GenAsyncSessionServiceFactory.getGenAsyncService(serviceName);
        GetResponse responseGet = service.getRequest(BuilderFactory.getRequestObjectBuilder(serviceName).buildGetRequest(origin, msgQuery, tackQuery), responseGetHeader);
        writeToFile(toByteArray(responseGet), "getResponse");
        return responseGet;
    }

    /**
     * parameters used to call generic async.
     *
     * @author EHP
     */
    public static class PostParameter {

        /**
         * String which identifies the request, to be used in InputReference and as second part of the kmehr id.
         */
        private String requestIdentifier;

        /**
         * the Blob object to send with the business content.
         */
        private Blob blob;

        /**
         * boolean indicating its a test.
         */
        private Boolean istest;

        /**
         * the service name , used to retrieve parameters from config file.
         */
        private String serviceName;

        /**
         * boolean indicating if we must use xades or not. currently Xades is not supported and value true will cause an
         * UnsupportedOperationException.
         */
        private Boolean useXades;

        /**
         * optional : the oaNumber to set in the to part of the header.
         */
        private Integer oaNumber;

        /**
         * the url to use for the
         */
        private String addressingHeaderUrl;

        /**
         *
         */
        public PostParameter(Blob blob, Boolean istest, String serviceName, Boolean useXades, Integer oaNumber, String addressingHeaderUrl, String requestIdentifier) {
            this.blob = blob;
            this.istest = istest;
            this.serviceName = serviceName;
            this.useXades = useXades;
            this.oaNumber = oaNumber;
            this.addressingHeaderUrl = addressingHeaderUrl;
            this.requestIdentifier = requestIdentifier;
        }

        /**
         * @param blob the blob to set
         */
        public void setBlob(Blob blob) {
            this.blob = blob;
        }

        /**
         * @param istest the istest to set
         */
        public void setIstest(Boolean istest) {
            this.istest = istest;
        }

        /**
         * @param serviceName the serviceName to set
         */
        public void setServiceName(String serviceName) {
            this.serviceName = serviceName;
        }

        /**
         * @param useXades the useXades to set
         */
        public void setUseXades(Boolean useXades) {
            this.useXades = useXades;
        }

        /**
         * @param oaNumber the oaNumber to set
         */
        public void setOaNumber(Integer oaNumber) {
            this.oaNumber = oaNumber;
        }

        /**
         * @param addressingHeaderUrl the addressingHeaderUrl to set
         */
        public void setAddressingHeaderUrl(String addressingHeaderUrl) {
            this.addressingHeaderUrl = addressingHeaderUrl;
        }
    }

    public static void writeToFile(byte[] content, String fileName) throws TechnicalConnectorException, IOException {
        String userHome = System.getProperty("user.home");
        if (userHome == null) {
            throw new IllegalStateException("user.home==null");
        }
        File home = new File(userHome);
        File sendFilesDir = new File(home, "genAsyncSendFiles");
        sendFilesDir.mkdir();
        java.io.File contentFile = File.createTempFile(fileName, ".xml", sendFilesDir);
        contentFile.createNewFile();
        LOG.debug("writing content to file on location " + contentFile.getAbsolutePath());
        FileWriter writer = new FileWriter(contentFile);
        writer.write(new String(content));
        writer.flush();
        writer.close();
    }


    public static PostResponse post(PostParameter parameterObject) throws Exception {
        LOG.debug("Creation of the post");
        if (parameterObject.useXades) {
            throw new UnsupportedOperationException("test utility does not support xades yet ");
        }
        CommonInput ci = Mappers.getMapper(CommonInputMapper.class).map(RequestBuilderFactory.getCommonBuilder("genericasync").createCommonInput(McnConfigUtil.retrievePackageInfo("genericasync." + parameterObject.serviceName), parameterObject.istest, parameterObject.requestIdentifier));
        be.cin.types.v1.Blob det = DomainBlobMapper.mapBlobToCinBlob(parameterObject.blob);
        // no xades needed for dmg async
        Post post = BuilderFactory.getRequestObjectBuilder(parameterObject.serviceName).buildPostRequest(ci, det, null);

        LOG.debug("Send of the post request");
        GenAsyncService service = GenAsyncSessionServiceFactory.getGenAsyncService(parameterObject.serviceName);

        WsAddressingHeader header = null;
        if (parameterObject.oaNumber != null) {
            header = WsAddressingUtil.createHeader(parameterObject.oaNumber.toString(), parameterObject.addressingHeaderUrl, null);
        } else {
            header = WsAddressingUtil.createHeader(null, parameterObject.addressingHeaderUrl, null);
        }

        PostResponse responsePost = service.postRequest(post, header);
        storePostAndResponseInFiles(post, responsePost, parameterObject.serviceName, parameterObject.blob);
        writeToFile(toByteArray(responsePost), "responsePost");

        LOG.debug("Call of handler for the post operation");
        ResponseObjectBuilder responseBuilder = BuilderFactory.getResponseObjectBuilder();
        boolean hasWarnings = responseBuilder.handlePostResponse(responsePost);
        if (hasWarnings) {
            LOG.info("The post has warnings");
        } else {
            LOG.info("The post has no warnings");
        }
        return responsePost;
    }


    private static void storePostAndResponseInFiles(Post post, PostResponse responsePost, String serviceName, Blob blob) throws TechnicalConnectorException, IOException {

        writeToFile(toByteArray(post), serviceName + post.getCommonInput().getInputReference() + "PostObject");
        writeToFile(toByteArray(responsePost), serviceName + post.getCommonInput().getInputReference() + "responsePost");
        String fileName = serviceName + post.getCommonInput().getInputReference() + "blobContent";
        byte[] content = blob.getContent();
        if ("none".equals(blob.getContentEncoding())) {
            writeToFile(content, fileName);
        } else {
            LOG.debug("blob contents:" + new String(content));
            byte[] decompress = ConnectorIOUtils.decompress(content);
            LOG.debug("blob contents decompressed:" + new String(decompress));
            writeToFile(decompress, fileName);
        }

    }


}
