package at.procon.eventhub.tachograph.config;

import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

@Configuration
@ConditionalOnExpression("T(org.springframework.util.StringUtils).hasText('${eventhub.tachograph.datasource.jdbc-url:}')")
public class TachographDataSourceConfig {

    private static final String SQL_SERVER_DRIVER_CLASS = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
    private static final String SQL_SERVER_JDBC_PREFIX = "jdbc:sqlserver://";

    @Bean
    @ConfigurationProperties(prefix = "eventhub.tachograph.datasource")
    public TachographDataSourceProperties tachographDataSourceProperties() {
        return new TachographDataSourceProperties();
    }

    @Bean(defaultCandidate = false)
    public DataSource tachographDataSource(TachographDataSourceProperties config) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl(validateJdbcUrl(config));
        dataSource.setUsername(config.getUsername());
        dataSource.setPassword(config.getPassword());
        String driverClassName = trimToNull(config.getDriverClassName());
        if (driverClassName != null) {
            dataSource.setDriverClassName(driverClassName);
        }
        return dataSource;
    }

    @Bean
    public NamedParameterJdbcTemplate tachographNamedParameterJdbcTemplate(
            @Qualifier("tachographDataSource") DataSource tachographDataSource
    ) {
        return new NamedParameterJdbcTemplate(tachographDataSource);
    }

    private String validateJdbcUrl(TachographDataSourceProperties config) {
        String jdbcUrl = trimToNull(config.getJdbcUrl());
        if (jdbcUrl == null) {
            throw new IllegalStateException("eventhub.tachograph.datasource.jdbc-url must not be empty");
        }

        String driverClassName = trimToNull(config.getDriverClassName());
        if (SQL_SERVER_DRIVER_CLASS.equals(driverClassName) && !jdbcUrl.startsWith(SQL_SERVER_JDBC_PREFIX)) {
            if (jdbcUrl.startsWith("jdbc:")) {
                String suggestedUrl = SQL_SERVER_JDBC_PREFIX + jdbcUrl.substring("jdbc:".length());
                throw new IllegalStateException(
                        "Invalid SQL Server JDBC URL '" + jdbcUrl + "'. Expected prefix '"
                                + SQL_SERVER_JDBC_PREFIX + "'. Example: " + suggestedUrl
                );
            }
            throw new IllegalStateException(
                    "Invalid SQL Server JDBC URL '" + jdbcUrl + "'. Expected prefix '"
                            + SQL_SERVER_JDBC_PREFIX + "'"
            );
        }

        return jdbcUrl;
    }

    private String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmedValue = value.trim();
        return trimmedValue.isEmpty() ? null : trimmedValue;
    }

    public static class TachographDataSourceProperties {
        private String jdbcUrl;
        private String username;
        private String password;
        private String driverClassName;

        public String getJdbcUrl() {
            return jdbcUrl;
        }

        public void setJdbcUrl(String jdbcUrl) {
            this.jdbcUrl = jdbcUrl;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getDriverClassName() {
            return driverClassName;
        }

        public void setDriverClassName(String driverClassName) {
            this.driverClassName = driverClassName;
        }
    }
}
