/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2024 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.task;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

import org.jboss.installer.auto.AutomaticInstallationParsingException;
import org.jboss.installer.core.FlatListPostInstallConfig;

public class DatasourceConfig extends FlatListPostInstallConfig {

    public static final String DS_PASSWORD_PROPERTY_NAME = "datasource.password";
    public static final String XA_RECOVERY_PASSWORD_PROPERTY_NAME = "xa_recovery.password";
    public static final String XA_PROPERTY_PREFIX = "xa-property-";
    private String dsName;
    private String jndiName;
    private String connectionUrl;
    private String minPool;
    private String maxPool;
    private String securityDomain;
    private String dsUsername;
    private String dsPassword;
    private String xaUsername;
    private String xaPassword;
    private List<XaProperty> xaProperties;

    public DatasourceConfig() {
    }

    public DatasourceConfig(String dsName, String jndiName, String minPool, String maxPool) {
        this.dsName = dsName;
        this.jndiName = jndiName;
        this.minPool = minPool;
        this.maxPool = maxPool;
    }

    public void setConnectionUrl(String connectionUrl) {
        this.connectionUrl = connectionUrl;
    }

    public void setSecurityDomain(String securityDomain) {
        this.securityDomain = securityDomain;
    }

    public void setDsUsername(String dsUsername) {
        this.dsUsername = dsUsername;
    }

    public void setDsPassword(String dsPassword) {
        this.dsPassword = dsPassword;
    }

    public void setXaUsername(String xaUsername) {
        this.xaUsername = xaUsername;
    }

    public void setXaPassword(String xaPassword) {
        this.xaPassword = xaPassword;
    }

    public void setXaProperties(List<XaProperty> xaProperties) {
        this.xaProperties = xaProperties;
    }

    public String getDsName() {
        return dsName;
    }

    public String getJndiName() {
        return jndiName;
    }

    public String getConnectionUrl() {
        return connectionUrl;
    }

    public String getMinPool() {
        return minPool;
    }

    public String getMaxPool() {
        return maxPool;
    }

    public String getSecurityDomain() {
        return securityDomain;
    }

    public String getDsUsername() {
        return dsUsername;
    }

    public String getDsPassword() {
        return dsPassword;
    }

    public String getXaUsername() {
        return xaUsername;
    }

    public String getXaPassword() {
        return xaPassword;
    }

    public List<XaProperty> getXaProperties() {
        return xaProperties;
    }

    @Override
    protected Map<String, String> listAttributes() {
        final HashMap<String, String> attrs = new HashMap<>();
        attrs.put("dsName", dsName);
        attrs.put("jndiName", jndiName);
        attrs.put("connectionUrl", connectionUrl);
        attrs.put("minPool", minPool);
        attrs.put("maxPool", maxPool);
        attrs.put("securityDomain", securityDomain);
        attrs.put("dsUsername", dsUsername);
        attrs.put("xaUsername", xaUsername);
        if (xaProperties != null) {
            for (XaProperty property : xaProperties) {
                attrs.put(XA_PROPERTY_PREFIX + property.getKey(), property.getValue());
            }
        }
        return attrs;
    }

    @Override
    protected void acceptAttributes(Map<String, String> attributes, BiFunction<String, String, String> variableResolver) throws AutomaticInstallationParsingException {
        dsName = attributes.get("dsName");
        jndiName = attributes.get("jndiName");
        connectionUrl = attributes.get("connectionUrl");
        minPool = attributes.get("minPool");
        maxPool = attributes.get("maxPool");
        securityDomain = attributes.get("securityDomain");
        dsUsername = attributes.get("dsUsername");
        dsPassword = variableResolver.apply(DS_PASSWORD_PROPERTY_NAME, "Datasource password:");
        xaUsername = attributes.get("xaUsername");
        if (xaUsername != null) {
            xaPassword = variableResolver.apply(XA_RECOVERY_PASSWORD_PROPERTY_NAME, "XA recovery password:");
        }
        xaProperties = getXaPropertiesFromAttributes(attributes);
    }

    @Override
    protected Set<String> listVariables() {
        final HashSet<String> vars = new HashSet<>();
        if (dsPassword != null) {
            vars.add(DS_PASSWORD_PROPERTY_NAME);
        }
        if (xaPassword != null) {
            vars.add(XA_RECOVERY_PASSWORD_PROPERTY_NAME);
        }
        return vars;
    }

    @Override
    public int hashCode() {
        return Objects.hash(dsName, jndiName, connectionUrl, minPool, maxPool, securityDomain, dsUsername,
                dsPassword, xaUsername, xaPassword, xaProperties);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        DatasourceConfig config = (DatasourceConfig) obj;
        return Objects.equals(dsName, config.dsName) && Objects.equals(jndiName, config.jndiName) &&
                Objects.equals(connectionUrl, config.connectionUrl) && Objects.equals(minPool, config.minPool) &&
                Objects.equals(maxPool, config.maxPool) && Objects.equals(securityDomain, config.securityDomain) &&
                Objects.equals(dsUsername, config.dsUsername) && Objects.equals(dsPassword, config.dsPassword) &&
                Objects.equals(xaUsername, config.xaUsername) && Objects.equals(xaPassword, config.xaPassword) &&
                Objects.equals(xaProperties, config.xaProperties);
    }

    private List<XaProperty> getXaPropertiesFromAttributes(Map<String, String> attributes) {
        final List<XaProperty> xaProperties = attributes.entrySet().stream()
                .filter(entry -> entry.getKey().startsWith(XA_PROPERTY_PREFIX))
                .map(entry -> new XaProperty(entry.getKey().replace(XA_PROPERTY_PREFIX, ""), entry.getValue()))
                .collect(Collectors.toList());
        return xaProperties.isEmpty() ? null : xaProperties;
    }

    public static class XaProperty {
        private final String key;
        private final String value;

        public XaProperty(String key, String value) {
            this.key = key;
            this.value = value;
        }

        public String getKey() {
            return key;
        }

        public String getValue() {
            return value;
        }
    }
}
