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

import org.jboss.installer.common.InstallerDialogs;
import org.jboss.installer.postinstall.task.DatasourceConfig;

import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

public class DatasourceConnectionUtils {
    public static final String CONNECTION_ERROR_KEY = "datasource.validation.connection_error";
    private static final String DATASOURCE_VALIDATION_DRIVER_ERROR = "datasource.validation.driver_error";
    public static final String CONNECTION_SUCCESSFUL_KEY = "datasource.validation.connection_successful";
    private final LanguageUtils langUtils;

    public DatasourceConnectionUtils(LanguageUtils langUtils) {
        this.langUtils = langUtils;
    }

    public void testDatabaseConnection(String user, String password, String url, List<String> jarPaths, List<DatasourceConfig.XaProperty> xaProperties, DatabaseDriver driver, boolean isXaDatasource) {
        Properties props = new Properties();
        props.put("user", user);
        props.put("password", password);
        if (isXaDatasource) {
            url = buildUrlFromProperties(xaProperties, driver);
        }
        LoggerUtils.systemLog.trace("Testing database connection");
        try (URLClassLoader loader = URLClassLoader.newInstance(getJarUrlArray(jarPaths))){
            Class<?> driverClass = loader.loadClass(driver.getDriverClassName());
            Driver driverInstance = (Driver) driverClass.getDeclaredConstructor().newInstance();
            try (Connection connection = driverInstance.connect(url, props)) {
                if (!connection.isValid(1)) {
                    LoggerUtils.systemLog.debug("Unable to connect to the database - invalid connection");
                    InstallerDialogs.showErrorMessage(langUtils.getString(CONNECTION_ERROR_KEY), langUtils, null);
                    return;
                }
            } catch (SQLException e) {
                LoggerUtils.systemLog.debug("Unable to connect to the database", e);
                InstallerDialogs.showErrorMessage(langUtils.getString(CONNECTION_ERROR_KEY), langUtils, null);
                return;
            }
        } catch (Throwable e) {
            LoggerUtils.systemLog.debug("Unexpected error when connecting to the database", e);
            InstallerDialogs.showErrorMessage(langUtils.getString(DATASOURCE_VALIDATION_DRIVER_ERROR), langUtils, null);
            return;
        }
        InstallerDialogs.showInfoMessage(langUtils.getString(CONNECTION_SUCCESSFUL_KEY), langUtils, null);
    }

    public String buildUrlFromProperties(List<DatasourceConfig.XaProperty> propertyFields, DatabaseDriver driver) {
        String url = driver.getConnectionUrl();
        for (DatasourceConfig.XaProperty property : propertyFields) {
            url = url.replace(getMatchingKey(property.getKey()), property.getValue());
        }
        if (driver.equals(DatabaseDriver.ORACLE)) {
            return propertyFields.stream().filter(property -> property.getKey().equals("Url")).findFirst().get().getValue();
        }
        return url;
    }

    private String getMatchingKey(String key) {
        return key.replaceAll("([a-z])([A-Z]+)", "$1_$2").toUpperCase();
    }

    private URL[] getJarUrlArray(List<String> jars) {
        return jars.stream().map(path -> {
            try {
                if (FileUtils.isUrl(path)) {
                    return new URI(path).toURL();
                }
                return Paths.get(path).toUri().toURL();
            } catch (Exception e) {
                throw new InstallerRuntimeException("Failed to convert jar path to URL");
            }
        }).toArray(URL[]::new);
    }
}
