package com.atlassian.stash.internal.jdbc;

import com.atlassian.stash.internal.db.DbType;
import org.joda.time.Duration;

import javax.annotation.Nonnull;
import java.util.Properties;

/**
 * The system's {@link DataSourceConfiguration}, which is {@link MutableDataSourceConfiguration mutable} to allow
 * migrating the database at runtime.
 * <p>
 * Note: This class has been reviewed and approved for having mutable state. It is primarily to support database
 * migration, which requires <i>some</i> mechanism for updating the driver class name and URL at runtime. The
 * {@link DataSourceConfiguration} interface, which is how this object is generally injected, provides an immutable
 * fascia for the class, so to normal consumers it appears immutable.
 */
public class SystemDataSourceConfiguration implements MutableDataSourceConfiguration {

    private final long connectTimeout;

    private String driverClassName;
    private String password;
    private String url;
    private String user;

    public SystemDataSourceConfiguration(DataSourceConfiguration configuration, long connectTimeout) {
        this.connectTimeout = connectTimeout;

        driverClassName = configuration.getDriverClassName();
        password = configuration.getPassword();
        url = configuration.getUrl();
        user = configuration.getUser();
    }

    @Nonnull
    @Override
    public DataSourceConfiguration copy() {
        return new SimpleDataSourceConfiguration(driverClassName, url, user, password);
    }

    @Nonnull
    @Override
    public String getDriverClassName() {
        return driverClassName;
    }

    @Nonnull
    @Override
    public String getPassword() {
        return password;
    }

    @Nonnull
    @Override
    public Properties getProperties() {
        //If a connect timeout has been specified, apply the driver-specific property for it. For H2 and HSQL, no
        //timeout is applied. That's fine because they can't timeout anyway.
        Properties properties = new Properties();
        if (connectTimeout > 0L) {
            DbType.forDriver(getDriverClassName()).ifPresent(dbType -> {
                Duration timeout = Duration.standardSeconds(connectTimeout);

                properties.putAll(dbType.getPropertyMap(timeout));
            });
        }

        return properties;
    }

    @Nonnull
    @Override
    public String getUrl() {
        return url;
    }

    @Nonnull
    @Override
    public String getUser() {
        return user;
    }

    @Nonnull
    @Override
    public DataSourceConfiguration update(DataSourceConfiguration configuration) {
        //Capture the existing settings
        DataSourceConfiguration old = copy();

        //Update the settings with the provided values
        driverClassName = configuration.getDriverClassName();
        password = configuration.getPassword();
        url = configuration.getUrl();
        user = configuration.getUser();

        //Return the old settings
        return old;
    }

    @Override
    public String toString() {
        return user + "@" + url + " via " + driverClassName;
    }
}
