/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2021 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.installer.postinstall;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.jboss.installer.core.InstallationData;
import org.jboss.installer.core.InstallationFailedException;
import org.jboss.installer.core.InstallerRuntimeException;
import org.jboss.installer.core.LanguageUtils;

public class TaskRunnerWrapper {

    protected static final String TASK_LIB = "tasks-lib/jboss-eap-installer-tasks.jar";
    private final LanguageUtils languageUtils;
    private final PropertyChangeListener listener;

    public TaskRunnerWrapper(LanguageUtils languageUtils, PropertyChangeListener listener) {
        this.languageUtils = languageUtils;
        this.listener = listener;
    }

    public void execute(InstallationData installationData, PropertyChangeSupport propertyChangeSupport) throws InstallationFailedException {
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        PostInstallTaskRunner runner = null;
        Path tasksJarPath = null;
        Path tempClientFile = null;
        try {
            final ArrayList<URL> urls = new ArrayList<>();
            final Path serverPath = installationData.getTargetFolder();
            final Path clientJarPath = serverPath.resolve("bin").resolve("client").resolve("jboss-cli-client.jar");
            if (!Files.exists(clientJarPath)) {
                throw new RuntimeException("The client jar at " + clientJarPath + " was not found.");
            }
            // copy the cli client jar because if the server is re-provisioned (e.g. by installing myfaces FP) the original jar
            // will be changed and when updated tries to copy the new jar over we'll have a problem on Windows (as the original
            // jar will still be used by CL)
            tempClientFile = Files.createTempFile("jboss-cli-client", "jar");
            IOUtils.copy(clientJarPath.toUri().toURL(), tempClientFile.toFile());
            urls.add(tempClientFile.toUri().toURL());

            tasksJarPath = Files.createTempFile("jboss-eap-installer-tasks", ".jar");
            final URL tasksJar = this.getClass().getClassLoader().getResource(TASK_LIB);
            if (tasksJar == null) {
                throw new InstallerRuntimeException("Unable to find post installation tasks JAR at " + TASK_LIB);
            }
            IOUtils.copy(tasksJar, tasksJarPath.toFile());
            urls.add(tasksJarPath.toUri().toURL());

            final URLClassLoader tasksCL = new URLClassLoader(urls.toArray(new URL[]{}), contextClassLoader);
            Thread.currentThread().setContextClassLoader(tasksCL);

            final Class<?> aClass = Class.forName("org.jboss.installer.postinstall.PostInstallTaskRunnerImpl", true, tasksCL);
            final Constructor<?> constructor = aClass.getDeclaredConstructor(Path.class, LanguageUtils.class, PropertyChangeListener.class);
            runner = (PostInstallTaskRunner) constructor.newInstance(serverPath, languageUtils, listener);
            runner.execute(installationData, propertyChangeSupport);

            // TODO: handle all those exceptions...
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (tasksJarPath != null) {
                FileUtils.deleteQuietly(tasksJarPath.toFile());
            }
            if (tempClientFile != null) {
                FileUtils.deleteQuietly(tempClientFile.toFile());
            }
            if (runner != null) {
                runner.close();
            }
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }
}
