/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
 * <a>http://www.crunii.com</a>
 */
package com.crunii.micro.ds.single.jpa;

import com.crunii.micro.common.constants.DSConstants;
import com.crunii.micro.common.exception.BusinessException;
import com.crunii.micro.domain.common.BaseRepoImpl;
import com.crunii.micro.domain.common.hibernate.action.listener.EncryptColListener;
import com.crunii.micro.domain.common.hibernate.action.listener.OperLogListener;
import com.crunii.micro.ds.properties.HibernateProperties;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;

import javax.annotation.Resource;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;

/**
 * <pre>
 *  #数据库类型暂时支持PostgreSQL和MySQL(TeleDB)
 *  com.crunii.micro.common.constants.DSConstants
 * 	DATABASE_MYSQL = "MySQL";
 * 	DATABASE_POSTGRESQL = "PostgreSQL";
 * 	DATABASE_ORACLE = "Oracle";
 * 	DATABASE_INFORMIX = "Informix";
 *  DATABASE_UNKNOWN = "UNKNOWN";
 * </pre>
 *
 * @author 田平 create 2020/8/21 11:06
 */
@Configuration
@Setter
@Getter
@ConditionalOnBean(name = DSConstants.DB_MAIN + "DS")
@ConditionalOnProperty(name = "db.jpa.enabled", havingValue = "true", matchIfMissing = true)
@EnableJpaRepositories(
		entityManagerFactoryRef = DSConstants.DB_MAIN + "EMF",
		transactionManagerRef = DSConstants.DB_MAIN + "TransactionManager",
		basePackages = { DSConstants.DOMAIN_BASE_PKG + ".repo." + DSConstants.DB_MAIN , "com.crunii.micro.service.studycenter.jdbc","com.crunii.micro.service.oneaffair.jdbc","com.crunii.micro.service.youthcall.jdbc"},
		repositoryBaseClass = BaseRepoImpl.class)
public class MainSingleJpaConfiguration {

	/**
	 * 新加注意修改名字
	 */
	@Resource(name = DSConstants.DB_MAIN + "DS")
	private DataSource ds;

	@Autowired
	private HibernateProperties hiProps;

	@Value("${spring.enableOperLog:false}")
	private boolean enableOperLogGlobal;

	@Value("${druid." + DSConstants.DB_MAIN + ".enableOperLog:false}")
	private boolean enableOperLog;

	@Value("${spring.enableEncryptCol:false}")
	private boolean enableEncryptColGlobal;

	@Value("${druid." + DSConstants.DB_MAIN + ".enableEncryptCol:false}")
	private boolean enableEncryptCol;

	@Autowired(required = false)
	private OperLogListener operLogListener;

	@Autowired(required = false)
	private EncryptColListener encryptListener;

	@Value("${dbtype.main}")
	private String dbTypeMain;

	@Bean(DSConstants.DB_MAIN + "JpaVendorAdapter")
	@Qualifier(DSConstants.DB_MAIN + "JpaVendorAdapter")
	public JpaVendorAdapter regJpaVendorAdapter() {
		DmHibernateJpaVendorAdapter adapter = new DmHibernateJpaVendorAdapter();
		if (DSConstants.DATABASE_MYSQL.equalsIgnoreCase(dbTypeMain)) {
			adapter.setDatabase(Database.MYSQL);
		} else if (DSConstants.DATABASE_POSTGRESQL.equalsIgnoreCase(dbTypeMain)) {
			adapter.setDatabase(Database.POSTGRESQL);
		} else if(DSConstants.DATABASE_DM.equalsIgnoreCase(dbTypeMain)){
			//
		} else {
			throw new BusinessException("unsupported this dbTypeMain:{}", dbTypeMain);
		}
		return adapter;
	}

	@Bean(DSConstants.DB_MAIN + "EMF")
	@Qualifier(DSConstants.DB_MAIN + "EMF")
	public EntityManagerFactory entityManagerFactory() {
		Properties properties = new Properties();
		properties.put("hibernate.transaction.coordinator_class", "jdbc");
		//电信teledb支持sequence

		if (DSConstants.DATABASE_MYSQL.equalsIgnoreCase(dbTypeMain)) {
			properties.put("hibernate.dialect", hiProps.getDialect_teledb());
		} else if (DSConstants.DATABASE_POSTGRESQL.equalsIgnoreCase(dbTypeMain)) {
			properties.put("hibernate.dialect", hiProps.getDialect_postgre());
		}else if (DSConstants.DATABASE_DM.equalsIgnoreCase(dbTypeMain)) {
			properties.put("hibernate.dialect", hiProps.getDialect_dm());
		} else {
			throw new BusinessException("unsupported this dbTypeMain:{}", dbTypeMain);
		}

		properties.put("hibernate.show_sql", hiProps.isShow_sql());
		properties.put("hibernate.cache.use_query_cache", hiProps.isCache_use_query_cache());
		properties.put("hibernate.cache.use_second_level_cache", hiProps.isCache_use_second_level_cache());
		properties.put("hibernate.transaction.flush_before_completion", hiProps.isTransaction_flush_before_completion());
		properties.put("hibernate.temp.use_jdbc_metadata_defaults", hiProps.isTemp_use_jdbc_metadata_defaults());

		LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
		emf.setDataSource(ds);
		emf.setJpaVendorAdapter(regJpaVendorAdapter());
		emf.setPackagesToScan(DSConstants.DOMAIN_BASE_PKG + ".entity." + DSConstants.DB_MAIN, "com.crunii.micro.service.studycenter.dto.studycenter", "com.crunii.micro.service.oneaffair.dto.oneaffair","com.crunii.micro.service.youthcall.dto");
		emf.setJpaProperties(properties);
		emf.afterPropertiesSet();

		EntityManagerFactory entityManagerFactory = emf.getObject();

		if (enableOperLogGlobal && enableOperLog) {
			SessionFactoryImpl sf = entityManagerFactory.unwrap(SessionFactoryImpl.class);
			EventListenerRegistry registry = sf.getServiceRegistry().getService(EventListenerRegistry.class);
			registry.getEventListenerGroup(EventType.POST_INSERT).prependListener(operLogListener);
			registry.getEventListenerGroup(EventType.POST_UPDATE).prependListener(operLogListener);
			registry.getEventListenerGroup(EventType.POST_DELETE).prependListener(operLogListener);
		}

		if (enableEncryptColGlobal && enableEncryptCol) {
			SessionFactoryImpl sf = entityManagerFactory.unwrap(SessionFactoryImpl.class);
			EventListenerRegistry registry = sf.getServiceRegistry().getService(EventListenerRegistry.class);

			registry.getEventListenerGroup(EventType.PRE_INSERT).prependListener(encryptListener);
			registry.getEventListenerGroup(EventType.PRE_UPDATE).prependListener(encryptListener);
			registry.getEventListenerGroup(EventType.POST_LOAD).prependListener(encryptListener);
		}

		return entityManagerFactory;
	}


}
