package io.pivotal.cfapp.config;

import java.net.URI;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

import io.r2dbc.spi.ConnectionFactories;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryOptions;
import io.r2dbc.spi.Option;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Profile("cloud")
@Configuration
class CloudConfig {

    private static final List<String> SUPPORTED_SCHEMES = Arrays.asList(new String[] { "mysql", "postgresql"});
    private static final String VCAP_SERVICE_VARIABLE = "vcap.services.cf-butler-backend.credentials.uri";

    @Autowired
    private Environment env;

    @Bean
    @ConditionalOnProperty(VCAP_SERVICE_VARIABLE)
    ConnectionFactory connectionFactory() {
        R2dbcProperties properties = r2dbcProperties();
        ConnectionFactoryOptions.Builder builder = ConnectionFactoryOptions
                .parse(properties.getUrl()).mutate();
        String username = properties.getUsername();
        if (StringUtils.hasText(username)) {
            builder.option(ConnectionFactoryOptions.USER, username);
        }
        String password = properties.getPassword();
        if (StringUtils.hasText(password)) {
            builder.option(ConnectionFactoryOptions.PASSWORD, password);
        }
        String databaseName = properties.getName();
        if (StringUtils.hasText(databaseName)) {
            builder.option(ConnectionFactoryOptions.DATABASE, databaseName);
        }
        if (properties.getProperties() != null) {
            properties.getProperties()
                    .forEach((key, value) -> builder
                            .option(Option.valueOf(key), value));
        }
        return ConnectionFactories.get(builder.build());
    }

    // support for external R2DBC source is limited to providers that support URI scheme
    private R2dbcProperties r2dbcProperties() {
        URI uri = env.getRequiredProperty(VCAP_SERVICE_VARIABLE, URI.class);
        String scheme = uri.getScheme();
        log.info("Attempting to connnect to a {} database instance.", scheme);
        if (scheme.startsWith("postgres")) {
            scheme = "postgresql";
        }
        if (SUPPORTED_SCHEMES.contains(scheme)) {
            R2dbcProperties properties = new R2dbcProperties();
            properties.setName(uri.getPath().replaceAll("/",""));
            String[] userInfoParts = uri.getUserInfo().split(":");
            String username = userInfoParts[0];
            String password = userInfoParts[1];
            properties.setUsername(username);
            properties.setPassword(password);
            StringBuilder builder = new StringBuilder();
            builder.append(String.format("r2dbc:pool:%s://", scheme));
            builder.append(uri.getHost());
            if (uri.getPort() != -1) {
                builder.append(":" + uri.getPort());
            }
            builder.append(uri.getPath());
            properties.setUrl(builder.toString());
            return properties;
        } else {
            throw new IllegalStateException(String.format("Could not initialize R2DBC properties from bound %s service instance.", uri.getScheme()));
        }
    }
}