package cn.ibizlab.core.util.config;

import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.HostDistance;
import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.QueryOptions;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SocketOptions;
import com.datastax.driver.core.policies.DCAwareRoundRobinPolicy;
import com.datastax.driver.core.policies.DowngradingConsistencyRetryPolicy;
import com.datastax.driver.core.policies.LoadBalancingPolicy;
import com.datastax.driver.core.policies.RetryPolicy;
import com.datastax.driver.core.policies.TokenAwarePolicy;
import com.datastax.driver.mapping.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;

import static com.datastax.driver.core.schemabuilder.SchemaBuilder.createKeyspace;
import static com.datastax.driver.mapping.NamingConventions.*;

@Configuration
@ConditionalOnExpression("!''.equals('${cassandra.host:}')")
public class CassandraConfig {

  @Bean
  public Cluster cluster(
      @Value("${cassandra.host}") String host,
      @Value("${cassandra.cluster.name:cluster}") String clusterName,
      @Value("${cassandra.options.maxrequestsperconnection:64}") int maxRequestsPerConnection,
      @Value("${cassandra.options.newconnectionthreshold:100}") int newConnectionThreshold,
      @Value("${cassandra.options.coreconnectionsperhost:2}") int coreConnectionsPerHost,
      @Value("${cassandra.options.maxconnectionsperhost:6}") int maxConnectionsPerHost,
      @Value("${cassandra.port:9042}") int port) {
    if(StringUtils.isEmpty(host))
      return null;
    
      // InetSocketAddress addr=new InetSocketAddress("172.16.100.243",9042);
      // InetSocketAddress addr2=new InetSocketAddress("172.16.100.77",9042);
      // InetSocketAddress addr3=new InetSocketAddress("172.16.100.77",19042);

      // LoadBalancingPolicy lbp = new TokenAwarePolicy(
      //           DCAwareRoundRobinPolicy.builder().withLocalDc("myDC").build()
      //   );
 
        //读超时或连接超时设置
        SocketOptions so = new SocketOptions().setReadTimeoutMillis(200000).setConnectTimeoutMillis(3000);
 
        //连接池配置
        //PoolingOptions poolingOptions = new PoolingOptions().setConnectionsPerHost(HostDistance.LOCAL, 2, 3);
        //集群在同一个机房用HostDistance.LOCAL   不同的机房用HostDistance.REMOTE   忽略用HostDistance.IGNORED
        PoolingOptions poolingOptions= new PoolingOptions()
                .setMaxRequestsPerConnection(HostDistance.LOCAL, maxRequestsPerConnection)//每个连接最多允许64个并发请求
                .setNewConnectionThreshold(HostDistance.LOCAL, newConnectionThreshold)
                .setCoreConnectionsPerHost(HostDistance.LOCAL, coreConnectionsPerHost)//和集群里的每个机器都至少有2个连接
                .setMaxConnectionsPerHost(HostDistance.LOCAL, maxConnectionsPerHost);//和集群里的每个机器都最多有6个连接
 
        //查询配置
        //设置一致性级别ANY(0),ONE(1),TWO(2),THREE(3),QUORUM(4),ALL(5),LOCAL_QUORUM(6),EACH_QUORUM(7),SERIAL(8),LOCAL_SERIAL(9),LOCAL_ONE(10);
        //可以在每次生成查询statement的时候设置，也可以像这样全局设置
        QueryOptions queryOptions = new QueryOptions().setConsistencyLevel(ConsistencyLevel.ONE);
 
        //重试策略
        RetryPolicy retryPolicy = DowngradingConsistencyRetryPolicy.INSTANCE;
  
    return Cluster.builder()
    .addContactPoint(host)
    .withPort(port)        .withClusterName(clusterName)
        //.withLoadBalancingPolicy(lbp)
                .withSocketOptions(so)
                .withPoolingOptions(poolingOptions)
                .withQueryOptions(queryOptions)
                .withRetryPolicy(retryPolicy) 
        .build();
  }

  @Bean
  public Session session(Cluster cluster, @Value("${cassandra.keyspace:dst}") String keyspace)
      throws IOException {
    //    final Session session = cluster.connect(keyspace);
    if(StringUtils.isEmpty(keyspace))
      return null;
    final Session session = cluster.connect();
    setupKeyspace(session, keyspace);
    return session;
  }

  @Value("${cassandra.class:SimpleStrategy}")
  private String classStrategy;

  @Value("${cassandra.replication_factor:1}")
  private Integer replication_factor;

  private void setupKeyspace(Session session, String keyspace) throws IOException {
    final Map<String, Object> replication = new HashMap<>();
    replication.put("class", classStrategy);
    replication.put("replication_factor", replication_factor);
    session.execute(createKeyspace(keyspace).ifNotExists().with().replication(replication));
    session.execute("USE " + keyspace);
    //    String[] statements =
    // split(IOUtils.toString(getClass().getResourceAsStream("/cql/setup.cql")), ";");
    //    Arrays.stream(statements).map(statement -> normalizeSpace(statement) +
    // ";").forEach(session::execute);
  }

  @Bean
  public MappingManager mappingManager(Session session) {
    if(session==null)
      return null;
    final PropertyMapper propertyMapper =
        new DefaultPropertyMapper()
            .setNamingStrategy(new DefaultNamingStrategy(LOWER_CAMEL_CASE, LOWER_SNAKE_CASE));
    final MappingConfiguration configuration =
        MappingConfiguration.builder().withPropertyMapper(propertyMapper).build();
    return new MappingManager(session, configuration);
  }
}
