/**
 * 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.tx;

import com.crunii.micro.common.constants.DSConstants;
import lombok.Getter;
import lombok.Setter;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.interceptor.*;

import javax.annotation.Resource;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 田平 create 2020/8/21 11:06
 */
@Configuration
@Setter
@Getter
@ConditionalOnBean(name = DSConstants.DB_MAIN + "DS")
public class MainSingleTxConfiguration {

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

	@Autowired(required = false)
	@Qualifier(DSConstants.DB_MAIN + "EMF")
	private EntityManagerFactory emf;

	/**
	 * 默认使用jpa
	 *
	 * @return
	 */
	@Bean(DSConstants.DB_MAIN + "TransactionManager")
	@Qualifier(DSConstants.DB_MAIN + "TransactionManager")
	@ConditionalOnExpression("#{'${db.jpa.enabled:true}'=='true'}")
	public PlatformTransactionManager transactionManager() {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		transactionManager.setDataSource(ds);
		transactionManager.setEntityManagerFactory(emf);
		return transactionManager;
	}

	/**
	 * 不使用jpa，默认使用
	 * db.jpa.enabled=false
	 *
	 * @return
	 */
	@Bean(DSConstants.DB_MAIN + "TransactionManager")
	@Qualifier(DSConstants.DB_MAIN + "TransactionManager")
	@ConditionalOnExpression("#{'${db.jpa.enabled:true}'=='false'}")
	public PlatformTransactionManager dataSourceTransactionManager() {
		DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
		transactionManager.setDataSource(ds);
		return transactionManager;
	}

	@Bean(DSConstants.DB_MAIN + "TransactionInterceptor")
	@Qualifier(DSConstants.DB_MAIN + "TransactionInterceptor")
	public TransactionInterceptor txAdvice() {
		NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

		RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
		requiredTx.setRollbackRules(Arrays.asList(new RollbackRuleAttribute(Exception.class)));
		requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
		//单位 second
		requiredTx.setTimeout(300);

		Map<String, TransactionAttribute> txMap = new HashMap<>();
		txMap.put("do*", requiredTx);
		txMap.put("save*", requiredTx);
		txMap.put("create*", requiredTx);
		txMap.put("update*", requiredTx);
		txMap.put("remove*", requiredTx);
		txMap.put("delete*", requiredTx);
		txMap.put("insert*", requiredTx);

		RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
		readOnlyTx.setReadOnly(true);
		txMap.put("*", readOnlyTx);

		source.setNameMap(txMap);
		TransactionInterceptor txAdvice = new TransactionInterceptor((TransactionManager) transactionManager(), source);
		return txAdvice;
	}

	@Bean(DSConstants.DB_MAIN + "Advisor")
	@Qualifier(DSConstants.DB_MAIN + "Advisor")
	public Advisor txAdviceAdvisor() {
		AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
		pointcut.setExpression(DSConstants.AOP_POINTCUT_EXPRESSION);
		return new DefaultPointcutAdvisor(pointcut, txAdvice());
	}
}
