package com.elab.mongodb.config;

import com.elab.core.utils.DataUtils;
import com.elab.mongodb.config.property.MongoConfig;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author liuhx
 * @create 2019/06/06 11:31
 * @email liuhx@elab-plus.com
 **/
@Configuration
@EnableConfigurationProperties(value = MongoConfig.class)
public class MongoAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(value = {MongoTemplate.class})
    public MongoTemplate getMongoTemplate(@Autowired MongoConfig mongoConfig) throws UnknownHostException {
        String mongoClientHost = mongoConfig.getHost();
        String[] mongoClientHosts = mongoClientHost.split(",");
        int[] mongoClientPorts = {mongoConfig.getPort()};
        String mongodbName = mongoConfig.getDatabaseName();
        String clusterUrl = mongoConfig.getClusterUrl();
        String userName = mongoConfig.getUserName() == null ? "" : mongoConfig.getUserName();
        String password = mongoConfig.getPassword() == null ? "" : mongoConfig.getPassword();
    if (null != clusterUrl && clusterUrl.length() > 0) {
            String[] hostAndPorts = clusterUrl.split(",");
            mongoClientHosts = new String[hostAndPorts.length];
            mongoClientPorts = new int[hostAndPorts.length];
            for (int i = 0; i < hostAndPorts.length; i++) {
                String[] hostAndPortArray = hostAndPorts[i].split(":");
                mongoClientHosts[i] = hostAndPortArray[0];
                mongoClientPorts[i] = Integer.parseInt(hostAndPortArray[1]);
            }
            return buildMongoTemplateCluster(mongoClientHosts, mongoClientPorts, mongodbName, userName, password);
        } else {
            MongoCredential userCredentials =
                MongoCredential.createCredential(userName, mongoConfig.getDatabaseName(), password.toCharArray());
            return buildMongoTemplate(mongoClientHosts, mongoConfig.getPort(), mongodbName, userCredentials);
        }
    }

    private MongoTemplate buildMongoTemplate(String[] mongoClientHosts, int port, String mongodbName,
        MongoCredential userCredentials) throws UnknownHostException {
        List<ServerAddress> serverAddressList = new ArrayList<>();
        for (String host : mongoClientHosts) {
            serverAddressList.add(new ServerAddress(host, port));
        }
        MongoClient mongoClient = new MongoClient(serverAddressList);
        SimpleMongoDbFactory mongoDbFactory = null;
        if (userCredentials != null && StringUtils.isNotEmpty(userCredentials.getUserName())
            && userCredentials.getPassword() != null) {
            //mongodb://${mongo.username}:${mongo.password}@${mongo.uri}/${mongo.db}
            String uri =
                "mongodb://" + userCredentials.getUserName() + ":" + new String(userCredentials.getPassword()) + "@"
                    + mongoClientHosts[0] + ":" + port + "/" + mongodbName + "";
            MongoClientURI mongoClientURI = new MongoClientURI(uri);
            mongoDbFactory = new SimpleMongoDbFactory(mongoClientURI);
        } else {
            mongoDbFactory = new SimpleMongoDbFactory(mongoClient, mongodbName);
        }

        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory);
        convertClass(mongoTemplate);
        return mongoTemplate;
    }

    private MongoTemplate buildMongoTemplateCluster(String[] mongoClientHosts, int[] mongoClientPorts,
        String mongodbName, String userName, String password) {
        try {
            List<ServerAddress> serverAddressList = new ArrayList<>();
            for (int i = 0; i < mongoClientHosts.length; i++) {
                if (mongoClientPorts.length < i + 1) {
                    mongoClientPorts[i] = mongoClientPorts[i - 1];
                }
                serverAddressList.add(new ServerAddress(mongoClientHosts[i], mongoClientPorts[i]));
            }
            MongoCredential userCredentials =
                MongoCredential.createCredential(userName, mongodbName, password.toCharArray());
            List<MongoCredential> userCredentialsList = new ArrayList<>();
            userCredentialsList.add(userCredentials);
            MongoClient mongoClient = new MongoClient(serverAddressList, userCredentialsList);

            SimpleMongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongoClient, mongodbName);
            MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory);
            convertClass(mongoTemplate);
            return mongoTemplate;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private void convertClass(MongoTemplate mongoTemplate) {
        MongoConverter converter = mongoTemplate.getConverter();
        if (converter.getTypeMapper().isTypeKey("_class")) {
            ((MappingMongoConverter)converter).setTypeMapper(new DefaultMongoTypeMapper(null));
        }
    }

}
