package com.crm.rely.backend.aspect;

import com.crm.rely.backend.util.DataSourceNames;
import com.crm.rely.backend.util.DynamicDataSourceContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author houn
 * @Date 2024/12/24 14:00
 * @PackageName:com.crm.manager.configuration
 * @ClassName: DynamicDataSourceAspect
 * @Description: TODO
 */
@Aspect
@Component
@Slf4j
public class DynamicDataSourceAspect {

    // 数据源缓存，减少重复判断带来的性能开销
    private final ConcurrentHashMap<Method, String> dataSourceCache = new ConcurrentHashMap<>();

    @Value("${spring.dynamic-datasource.enable-auto-datasource-switch:false}")
    private boolean enableAutoDataSourceSwitch;

    @Pointcut("execution(* com.crm.*.dao.mapper.*.*(..)) || execution(* com.crm.*.dao.repository.*.*(..))")
    public void dataSourcePointCut() {
    }

    @Around("dataSourcePointCut()")
    public Object switchDataSource(ProceedingJoinPoint joinPoint) throws Throwable {
        // 如果动态数据源切换未启用，直接执行目标方法
        if (!enableAutoDataSourceSwitch) {
            return joinPoint.proceed();
        }

        // 获取当前方法对象
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        String methodName = method.getName();
        String className = method.getDeclaringClass().getSimpleName();

        // 判断是否有事务激活
        boolean transactionInherited = isTransactionInherited();

        if (!transactionInherited) {
            // 从缓存中获取数据源，未命中则动态解析
            String dataSource = dataSourceCache.computeIfAbsent(method, this::determineDataSource);

            log.info("切换数据源: {} -> 方法: {}.{}", dataSource, className, methodName);
            DynamicDataSourceContext.setDataSource(dataSource);
        } else {
            log.debug("事务已激活，保持现有数据源 -> 方法: {}.{}", className, methodName);
        }

        try {
            // 执行目标方法
            return joinPoint.proceed();
        } finally {
            // 如果事务未激活，清理数据源上下文，避免线程污染
            if (!transactionInherited) {
                DynamicDataSourceContext.clear();
                log.debug("清理数据源上下文 -> 方法: {}.{}", className, methodName);
            }
        }
    }

    /**
     * 判断是否是事务传递。
     *
     * @return true 如果事务是从调用者方法传递过来的；false 否则
     */
    private boolean isTransactionInherited() {
        return TransactionSynchronizationManager.isSynchronizationActive() &&
                TransactionSynchronizationManager.isActualTransactionActive();
    }

    /**
     * 根据方法名动态确定数据源。
     *
     * @param method 当前方法对象
     * @return 数据源名称
     */
    private String determineDataSource(Method method) {
        String methodName = method.getName().toLowerCase();

        if (methodName.startsWith("add") || methodName.startsWith("insert") || methodName.startsWith("update") ||
                methodName.startsWith("find") || methodName.startsWith("save") ||
                methodName.startsWith("delete")) {
            return DataSourceNames.MASTER;
        } else if (methodName.startsWith("get") || methodName.startsWith("query") ||
                methodName.startsWith("select") || methodName.startsWith("count")) {
            return DataSourceNames.SLAVE;
        }
        return DataSourceNames.MASTER; // 默认使用主数据源
    }
}