package net.northking.cctp.common.saas.db;

import com.zaxxer.hikari.HikariDataSource;
import net.northking.cctp.common.db.mode.*;
import net.northking.cctp.common.saas.cache.SaaSAppDataSourceClusterCache;
import net.northking.cctp.common.security.authentication.NKSecurityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 多集群动态数据源
 *
 * @author xiusong.xie
 * @since 2.4
 */
public class MultiClusterDynamicDataSource extends AbstractClusterDataSource
{
  /**
   * 日志
   */
  private final static Logger logger = LoggerFactory.getLogger(MultiClusterDynamicDataSource.class);

  /**
   * 数据源容器，key={clusterId}-{DataSourceCluster}
   */
  private static final Map<String, DataSourceCluster> DATA_SOURCE_CLUSTER_MAP = new HashMap<>();

  private List<DataSourceClusterProperties> multiClusterProperties = new ArrayList<>();

  public void setMultiClusterProperties(List<DataSourceClusterProperties> multiClusterProperties)
  {
    this.multiClusterProperties = multiClusterProperties;
  }

  private SaaSAppDataSourceClusterCache clusterCache;

  public void setClusterCache(SaaSAppDataSourceClusterCache clusterCache)
  {
    this.clusterCache = clusterCache;
  }

  private String appName;

  public void setAppName(String appName)
  {
    this.appName = appName;
  }

  @Override
  public void afterPropertiesSet()
  {
    if (multiClusterProperties == null || multiClusterProperties.isEmpty())
    {
      throw new IllegalArgumentException("未配置数据源多集群'nk.data-source.multi-cluster'");
    }
    for (DataSourceClusterProperties clusterProperties : multiClusterProperties)
    {
      DataSourceCluster cluster = createCluster(clusterProperties);
      DATA_SOURCE_CLUSTER_MAP.put(cluster.getName(), cluster);
    }
  }


  /**
   * 创建数据源集群
   */
  private DataSourceCluster createCluster(DataSourceClusterProperties clusterProperties)
  {
    DataSourceCluster cluster = new DataSourceCluster();
    cluster.setName(clusterProperties.getName());
    for (DataSourceConfig config : clusterProperties.getCluster())
    {
      HikariDataSource dataSource = createDataSource(config);
      if (config.isReadOnly())
      {
        cluster.putReadDataSource(config.getName(), dataSource);
      } else
      {
        cluster.putWriteDataSource(config.getName(), dataSource);
      }
    }
    return cluster;
  }

  /**
   * 决策数据源集群
   *
   * @return 数据源集群
   */
  protected DataSourceCluster determineTargetCluster() throws SQLException
  {
    String tenantId = NKSecurityContext.getTenantId();
    String clusterId = clusterCache.findDataSourceClusterId(tenantId, appName);
    if (StringUtils.isEmpty(clusterId))
    {
      throw new SQLException("数据源集群ID为NULL，无法获取数据源集群");
    }
    DataSourceCluster cluster = DATA_SOURCE_CLUSTER_MAP.get(clusterId);
    if (cluster == null)
    {
      throw new IllegalArgumentException("数据源集群(" + clusterId + ")不存在");
    }
    return cluster;
  }

  @Override
  public Connection getConnection() throws SQLException
  {
    Connection connection;
    if (DataBaseModeContext.isReadMode())
    {
      connection = determineTargetCluster().getReadConnection();
    } else
    {
      connection = determineTargetCluster().getWriteConnection();
    }

    DataBaseModeContext.unload();
    return connection;
  }

  @Override
  public Connection getConnection(String username, String password) throws SQLException
  {
    Connection connection;
    if (DataBaseModeContext.isReadMode())
    {
      connection = determineTargetCluster().getReadConnection(username, password);
    } else
    {
      connection = determineTargetCluster().getWriteConnection(username, password);
    }
    DataBaseModeContext.unload();
    return connection;
  }

}
