package com.collabnet.ctf.events;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;

@EventListener(version = SoapVersion.SOAP_60)
public class SynchronousHookScriptEventListener {

    private static String hookDirectory = "/opt/collabnet/teamforge/hooks/synchronous/";

    @Synchronous(topic = {"object.item.Artifact.update"})
    public void executeHookScript(final EventContext context) throws Exception {
        boolean intendedException = false;
        int returnCode = 0;

        File hookScript = HookUtil.getHookScript(hookDirectory, context.getEventTopic(),
                context.getEventOperation());
        context.logInfo("Going to call " + hookScript.getAbsolutePath() + " if it does exist.");
        try {

            if (hookScript.exists()) {

                Process runningProcess = runHookScript(hookScript, context);

                returnCode = runningProcess.waitFor();

                String errorMessage = processStream(runningProcess.getErrorStream());
                if (!errorMessage.equals("")) {
                    context.addErrorMessage(errorMessage);
                }

                String successMessage = processStream(runningProcess.getInputStream());
                if (!successMessage.equals("")) {
                    context.addSuccessMessage(successMessage);
                }

                if (returnCode != 0) {
                    intendedException = true;
                    throw new Exception("Hook script " + hookScript.getAbsolutePath() +
                            " rejected anticipated event with error code " + returnCode);
                }
            }

        } catch (Exception e) {
            if (!intendedException) {
                context.logError("Exception occured: " + e.getMessage(), e);
            } else {
                context.logWarn("Hook script " + hookScript.getAbsolutePath() +
                        " rejected anticipated event with error code " + returnCode);
                throw e;
            }
        }
    }

    /**
     * Process the stream from the process
     * @param inputStream
     * @return
     * @throws IOException
     */
    private String processStream(InputStream inputStream) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
        StringBuffer buffer = new StringBuffer();
        while (reader.ready()) {
            String line = reader.readLine();
            if (line == null) {
                break;
            }
            buffer.append(line);
        }
        return buffer.toString();
    }

    /**
     * Runt he hook script
     *
     * @param hookScript
     * @param context
     * @return
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws IOException
     */
    private Process runHookScript(File hookScript, final EventContext context)
            throws IllegalAccessException, InvocationTargetException, IOException {

        ProcessBuilder process = new ProcessBuilder(hookScript.getAbsolutePath());

        HookUtil.setEventContextInformationInEnvironment(context, process);

        HookUtil.setResultValuesInEnvironment(process, context.getOriginalData(), "original");
        HookUtil.setResultValuesInEnvironment(process, context.getUpdatedData(), "updated");

        Process runningProcess = process.start();

        runningProcess.getOutputStream().close();

        return runningProcess;
    }
}
