package link.at17.mid.tushare.component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.ParamNameResolver;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import link.at17.mid.tushare.annotation.BatchInsert;
import link.at17.mid.tushare.annotation.BatchList;
import lombok.RequiredArgsConstructor;
import lombok.Value;

/**
 * 批量插入切片器
 */
@Aspect
@Component
@RequiredArgsConstructor
public class BatchInsertAspect {

    private final SqlSessionFactory sqlSessionFactory;
    private final ConcurrentMap<Method, Meta> cache = new ConcurrentHashMap<>();

    @Around("@annotation(batchInsert)")
    public Object around(ProceedingJoinPoint pjp, BatchInsert batchInsert) throws Throwable {
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        Object[] args = pjp.getArgs();

        // 定位批量参数索引
        int listIdx = findBatchListIndex(method);
        List<?> list = (List<?>) args[listIdx];
        if (list == null || list.isEmpty()) {
            return pjp.proceed(); // 无数据，原样调用一次
        }

        // 计算并缓存批大小
        Meta meta = cache.computeIfAbsent(method, m -> computeMeta(m, args, listIdx, batchInsert.maxParams()));

        int batchSize = Math.max(1, meta.batchSize);
        // 分批执行
        if (method.getReturnType() == Void.TYPE) {
            for (int i = 0; i < list.size(); i += batchSize) {
                Object[] cloned = args.clone();
                cloned[listIdx] = list.subList(i, Math.min(i + batchSize, list.size()));
                pjp.proceed(cloned);
            }
            return null;
        }
        if (method.getReturnType() == int.class || method.getReturnType() == Integer.class) {
            int total = 0;
            for (int i = 0; i < list.size(); i += batchSize) {
                Object[] cloned = args.clone();
                cloned[listIdx] = list.subList(i, Math.min(i + batchSize, list.size()));
                total += (int) pjp.proceed(cloned);
            }
            return total;
        }
        throw new IllegalStateException("不支持的返回类型：" + method.getReturnType());
    }

    private int findBatchListIndex(Method method) {
        Annotation[][] pa = method.getParameterAnnotations();
        for (int i = 0; i < pa.length; i++) {
            for (Annotation a : pa[i]) {
                if (a.annotationType() == BatchList.class)
                    return i;
            }
        }
        throw new IllegalArgumentException(method + " 缺少 @BatchList 标注的集合参数");
    }

    private Meta computeMeta(Method method, Object[] args, int listIdx, int maxParams) {
        String statementId = method.getDeclaringClass().getName() + "." + method.getName();
        try (SqlSession session = sqlSessionFactory.openSession()) {
            MappedStatement ms = session.getConfiguration().getMappedStatement(statementId);

            ParamNameResolver resolver = new ParamNameResolver(ms.getConfiguration(), method);

            // 用真实参数计算 base/perRow：一次放1条，一次放2条，两者相减即为单条参数数量
            Object[] a1 = args.clone();
            Object sample = ((List<?>) args[listIdx]).get(0);
            a1[listIdx] = Collections.singletonList(sample);

            Object p1 = resolver.getNamedParams(a1);
            BoundSql bs1 = ms.getBoundSql(p1);
            int s1 = bs1.getParameterMappings().size();

            Object[] a2 = args.clone();
            a2[listIdx] = Arrays.asList(sample, sample);

            Object p2 = resolver.getNamedParams(a2);
            BoundSql bs2 = ms.getBoundSql(p2);
            int s2 = bs2.getParameterMappings().size();

            int perRow = s2 - s1;
            if (perRow <= 0)
                throw new IllegalStateException("无法识别每行占位符数量");
            int base = s1 - perRow;
            int batch = Math.max(1, (maxParams - base) / perRow);
            return new Meta(perRow, base, batch);
        }
    }

    @Value
    static class Meta {
        int perRow;
        int base;
        int batchSize;
    }
}