package com.storlead.framework.mybatis.datasource;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.RequiredArgsConstructor;
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.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * 子模块默认数据源：按配置包前缀 push 数据源；若目标已标注 {@link DS} 则跳过，由官方 {@code @DS} 切面处理。
 */
@Slf4j
@Aspect
@Component
@Order(0)
@RequiredArgsConstructor
public class ModuleDefaultDataSourceAspect {

    private final ModuleDefaultDataSourceProperties properties;

    @Pointcut("execution(* *(..)) && (within(com.storlead..service..*) || within(com.storlead..mapper..*))")
    public void moduleServiceOrMapperLayer() {
    }

    @Around("moduleServiceOrMapperLayer()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        if (!properties.isModuleDefaultEnabled()) {
            return joinPoint.proceed();
        }
        if (hasDsAnnotation(joinPoint)) {
            return joinPoint.proceed();
        }
        String datasource = resolveDatasource(joinPoint);
        if (!StringUtils.hasText(datasource)) {
            return joinPoint.proceed();
        }
        DynamicDataSourceContextHolder.push(datasource);
        try {
            if (log.isDebugEnabled()) {
                log.debug("module default datasource [{}] -> {}", datasource, joinPoint.getSignature().toShortString());
            }
            return joinPoint.proceed();
        } finally {
            DynamicDataSourceContextHolder.poll();
        }
    }

    private boolean hasDsAnnotation(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Class<?> declaringType = signature.getDeclaringType();
        if (AnnotatedElementUtils.hasAnnotation(method, DS.class)) {
            return true;
        }
        if (declaringType != null && AnnotatedElementUtils.hasAnnotation(declaringType, DS.class)) {
            return true;
        }
        return AnnotatedElementUtils.hasAnnotation(joinPoint.getTarget().getClass(), DS.class);
    }

    /**
     * 最长包前缀匹配
     */
    private String resolveDatasource(ProceedingJoinPoint joinPoint) {
        String className = resolveBusinessClassName(joinPoint);
        String matchedDs = null;
        int matchedLen = -1;
        for (ModuleDefaultDataSourceProperties.ModuleDefaultRule rule : properties.getModuleDefaults()) {
            if (rule.getPackages() == null || !StringUtils.hasText(rule.getDatasource())) {
                continue;
            }
            for (String pkg : rule.getPackages()) {
                if (!StringUtils.hasText(pkg)) {
                    continue;
                }
                if (matchesPackage(className, pkg.trim()) && pkg.length() > matchedLen) {
                    matchedLen = pkg.length();
                    matchedDs = rule.getDatasource();
                }
            }
        }
        return matchedDs;
    }

    private boolean matchesPackage(String className, String packagePrefix) {
        return className.equals(packagePrefix)
                || className.startsWith(packagePrefix + ".");
    }

    /**
     * Mapper 为 JDK 代理时，用接口全限定名匹配包前缀。
     */
    private String resolveBusinessClassName(ProceedingJoinPoint joinPoint) {
        if (joinPoint.getSignature() instanceof MethodSignature) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Class<?> declaringType = signature.getDeclaringType();
            if (declaringType != null && declaringType.getName().startsWith("com.storlead.")) {
                return declaringType.getName();
            }
        }
        return joinPoint.getTarget().getClass().getName();
    }
}
