/*
 * 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 static org.wildfly.security.ssl.CipherSuiteSelector.OPENSSL_DEFAULT_CIPHER_SUITE_NAMES;

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

import org.apache.commons.lang3.StringUtils;
import org.jboss.installer.auto.AutomaticInstallationParsingException;
import org.jboss.installer.core.FlatListPostInstallConfig;

public abstract class AbstractHttpsEnableConfig extends FlatListPostInstallConfig {
    public static final String PROTOCOL_ATTRIBUTE_KEY = "protocol";
    public static final String CIPHER_SUITES_ATTRIBUTE_KEY = "cipherSuites";
    public static final String CIPHER_NAMES_TLS13_ATTRIBUTE_KEY = "tls13CipherNames";
    public static final String KEYSTORE_PATH_ATTRIBUTE_KEY = "keyStorePath";
    public static final String MUTUAL_ATTRIBUTE_KEY = "mutual";
    public static final String TRUSTSTORE_PATH_ATTRIBUTE_KEY = "trustStorePath";
    public static final String DO_GENERATE_STORE_ATTRIBUTE_KEY = "doGenerateStore";
    public static final String DN_ATTRIBUTE_KEY = "dn";
    public static final String VALIDITY_ATTRIBUTE_KEY = "validity";
    public static final String CLIENT_CERT_PATH_ATTRIBUTE_KEY = "clientCertPath";
    public static final String VALIDATE_CERTIFICATE_ATTRIBUTE_KEY = "validateCertificate";
    public static final String KEYSTORE_PASSWORD_VARIABLE_KEY = "keystorePassword";
    public static final String KEYSTORE_PASSWORD_DESC = "Keystore password";
    public static final String TRUSTSTORE_PASSWORD_VARIABLE_KEY = "truststorePassword";
    public static final String TRUSTSTORE_PASSWORD_DESC = "Truststore password";
    public static final String DEFAULT_CIPHER_NAMES_TLS13 = OPENSSL_DEFAULT_CIPHER_SUITE_NAMES;
    public static final String PROTOCOL_TLSv13 = "TLSv1.3";
    public static final String DEFAULT_CIPHER_SUITES_TLS12 = "DEFAULT";
    public static final String DN_DEFAULT = "Unknown";
    public static final String PROTOCOL_TLSv12 = "TLSv1.2";

    private Set<String> protocols;
    private String cipherSuites;
    private String keyStorePath;
    private String keyStorePassword;
    private boolean mutual;
    private String trustStorePath;
    private String trustStorePassword;
    private boolean doGenerateStore;
    private String dn;
    private String validity;
    private String clientCertPath;
    private boolean validateCertificate;

    private boolean keystoreGeneratedForStandalone = false;
    private boolean keystoreGeneratedForDomain = false;
    private boolean clientCertImportedForStandalone = false;
    private boolean clientCertImportedForDomain = false;
    private String tls13cipherNames;

    public AbstractHttpsEnableConfig() {
        // no-op for unmarshalling
    }

    public AbstractHttpsEnableConfig(Set<String> protocols, String cipherSuites, String tls13cipherNames, String keyStorePath,
                                     String keyStorePassword, boolean mutual, String trustStorePath, String trustStorePassword,
                                     boolean doGenerateStore, String dn, String validity, String clientCertPath, boolean validateCertificate) {
        this.protocols = protocols;
        this.cipherSuites = cipherSuites;
        this.tls13cipherNames = tls13cipherNames;
        this.keyStorePath = keyStorePath;
        this.keyStorePassword = keyStorePassword;
        this.mutual = mutual;
        this.trustStorePath = trustStorePath;
        this.trustStorePassword = trustStorePassword;
        this.doGenerateStore = doGenerateStore;
        this.dn = dn;
        this.validity = validity;
        this.clientCertPath = clientCertPath;
        this.validateCertificate = validateCertificate;
    }

    public Set<String> getProtocols() {
        return protocols;
    }

    public String getCipherSuites() {
        return cipherSuites;
    }

    public String getTls13cipherNames() {
        return tls13cipherNames;
    }

    public void setTls13cipherNames(String tls13cipherNames) {
        this.tls13cipherNames = tls13cipherNames;
    }

    public String getKeyStorePath() {
        return keyStorePath;
    }

    public String getKeyStorePassword() {
        return keyStorePassword;
    }

    public boolean isMutual() {
        return mutual;
    }

    public String getTrustStorePath() {
        return trustStorePath;
    }

    public String getTrustStorePassword() {
        return trustStorePassword;
    }

    public boolean isDoGenerateStore() {
        return doGenerateStore;
    }

    public String getDn() {
        return dn;
    }

    public String getValidity() {
        return validity;
    }

    public String getClientCertPath() {
        return clientCertPath;
    }

    public boolean isValidateCertificate() {
        return validateCertificate;
    }

    public boolean isKeystoreGeneratedForStandalone() {
        return keystoreGeneratedForStandalone;
    }

    public void setKeystoreGeneratedForStandalone(boolean keystoreGeneratedForStandalone) {
        this.keystoreGeneratedForStandalone = keystoreGeneratedForStandalone;
    }

    public boolean isKeystoreGeneratedForDomain() {
        return keystoreGeneratedForDomain;
    }

    public void setKeystoreGeneratedForDomain(boolean keystoreGeneratedForDomain) {
        this.keystoreGeneratedForDomain = keystoreGeneratedForDomain;
    }

    public boolean isClientCertImportedForStandalone() {
        return clientCertImportedForStandalone;
    }

    public void setClientCertImportedForStandalone(boolean clientCertImportedForStandalone) {
        this.clientCertImportedForStandalone = clientCertImportedForStandalone;
    }

    public boolean isClientCertImportedForDomain() {
        return clientCertImportedForDomain;
    }

    public void setClientCertImportedForDomain(boolean clientCertImportedForDomain) {
        this.clientCertImportedForDomain = clientCertImportedForDomain;
    }

    @Override
    protected Map<String, String> listAttributes() {
        final HashMap<String, String> attrs = new HashMap<>();
        attrs.put(PROTOCOL_ATTRIBUTE_KEY, protocols != null ? StringUtils.join(protocols, ",") : null);
        attrs.put(CIPHER_SUITES_ATTRIBUTE_KEY, cipherSuites);
        attrs.put(CIPHER_NAMES_TLS13_ATTRIBUTE_KEY, tls13cipherNames);
        attrs.put(KEYSTORE_PATH_ATTRIBUTE_KEY, keyStorePath);
        attrs.put(MUTUAL_ATTRIBUTE_KEY, Boolean.toString(mutual));
        attrs.put(TRUSTSTORE_PATH_ATTRIBUTE_KEY, trustStorePath);
        attrs.put(DO_GENERATE_STORE_ATTRIBUTE_KEY, Boolean.toString(doGenerateStore));
        attrs.put(DN_ATTRIBUTE_KEY, dn);
        attrs.put(VALIDITY_ATTRIBUTE_KEY, validity);
        attrs.put(CLIENT_CERT_PATH_ATTRIBUTE_KEY, clientCertPath);
        attrs.put(VALIDATE_CERTIFICATE_ATTRIBUTE_KEY, Boolean.toString(validateCertificate));
        return attrs;
    }

    @Override
    protected Set<String> listVariables() {
        final HashSet<String> vars = new HashSet<>();
        if (keyStorePath != null && !keyStorePath.isEmpty()) {
            vars.add(KEYSTORE_PASSWORD_VARIABLE_KEY);
        }
        if (trustStorePath != null && !trustStorePath.isEmpty()) {
            vars.add(TRUSTSTORE_PASSWORD_VARIABLE_KEY);
        }
        return vars;
    }

    @Override
    protected void acceptAttributes(Map<String, String> attributes, BiFunction<String, String, String> variableResolver) throws AutomaticInstallationParsingException {
        protocols = attributes.get(PROTOCOL_ATTRIBUTE_KEY) != null ? Set.of(StringUtils.split(attributes.get(PROTOCOL_ATTRIBUTE_KEY), ',')) : null;
        cipherSuites = attributes.get(CIPHER_SUITES_ATTRIBUTE_KEY);
        tls13cipherNames = attributes.get(CIPHER_NAMES_TLS13_ATTRIBUTE_KEY);
        keyStorePath = attributes.get(KEYSTORE_PATH_ATTRIBUTE_KEY);
        mutual = Boolean.parseBoolean(attributes.get(MUTUAL_ATTRIBUTE_KEY));
        trustStorePath = attributes.get(TRUSTSTORE_PATH_ATTRIBUTE_KEY);
        doGenerateStore = Boolean.parseBoolean(attributes.get(DO_GENERATE_STORE_ATTRIBUTE_KEY));
        dn = attributes.get(DN_ATTRIBUTE_KEY);
        validity = attributes.get(VALIDITY_ATTRIBUTE_KEY);
        clientCertPath = attributes.get(CLIENT_CERT_PATH_ATTRIBUTE_KEY);
        validateCertificate = Boolean.parseBoolean(attributes.get(VALIDATE_CERTIFICATE_ATTRIBUTE_KEY));
        if (keyStorePath != null && !keyStorePath.isEmpty()) {
            keyStorePassword = variableResolver.apply(KEYSTORE_PASSWORD_VARIABLE_KEY, KEYSTORE_PASSWORD_DESC);
        }
        if (trustStorePath != null && !trustStorePath.isEmpty()) {
            trustStorePassword = variableResolver.apply(TRUSTSTORE_PASSWORD_VARIABLE_KEY, TRUSTSTORE_PASSWORD_DESC);
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AbstractHttpsEnableConfig config = (AbstractHttpsEnableConfig) o;
        return mutual == config.mutual && doGenerateStore == config.doGenerateStore && validateCertificate == config.validateCertificate && keystoreGeneratedForStandalone == config.keystoreGeneratedForStandalone && keystoreGeneratedForDomain == config.keystoreGeneratedForDomain && clientCertImportedForStandalone == config.clientCertImportedForStandalone && clientCertImportedForDomain == config.clientCertImportedForDomain && Objects.equals(protocols, config.protocols) && Objects.equals(cipherSuites, config.cipherSuites) && Objects.equals(keyStorePath, config.keyStorePath) && Objects.equals(keyStorePassword, config.keyStorePassword) && Objects.equals(trustStorePath, config.trustStorePath) && Objects.equals(trustStorePassword, config.trustStorePassword) && Objects.equals(dn, config.dn) && Objects.equals(validity, config.validity) && Objects.equals(clientCertPath, config.clientCertPath);
    }

    @Override
    public int hashCode() {
        return Objects.hash(protocols, cipherSuites, keyStorePath, keyStorePassword, mutual, trustStorePath, trustStorePassword, doGenerateStore, dn, validity, clientCertPath, validateCertificate, keystoreGeneratedForStandalone, keystoreGeneratedForDomain, clientCertImportedForStandalone, clientCertImportedForDomain);
    }

    @Override
    public String toString() {
        return "Config{" +
                "protocols=" + protocols +
                ", cipherSuites='" + cipherSuites + '\'' +
                ", keyStorePath='" + keyStorePath + '\'' +
                ", keyStorePassword='" + keyStorePassword + '\'' +
                ", mutual=" + mutual +
                ", trustStorePath='" + trustStorePath + '\'' +
                ", trustStorePassword='" + trustStorePassword + '\'' +
                ", doGenerateStore=" + doGenerateStore +
                ", dn='" + dn + '\'' +
                ", validity='" + validity + '\'' +
                ", clientCertPath='" + clientCertPath + '\'' +
                ", validateCertificate=" + validateCertificate +
                ", keystoreGeneratedForStandalone=" + keystoreGeneratedForStandalone +
                ", keystoreGeneratedForDomain=" + keystoreGeneratedForDomain +
                ", clientCertImportedForStandalone=" + clientCertImportedForStandalone +
                ", clientCertImportedForDomain=" + clientCertImportedForDomain +
                ", tls13cipherNames='" + tls13cipherNames + '\'' +
                '}';
    }
}
