package com.CUST.brain.common.aspect;

import com.CUST.brain.common.annotation.RedisCache;
import com.CUST.brain.common.enums.CacheUpdateStrategy;
import com.CUST.brain.common.constant.RedisKeyConstants;
import com.CUST.brain.common.exception.BusinessException;
import com.CUST.brain.common.utils.RedisUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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.reflect.MethodSignature;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.lang.reflect.Type;
import com.fasterxml.jackson.core.type.TypeReference;

/**
 * Redis缓存切面
 */
@Slf4j
@Aspect
@Component
public class RedisCacheAspect {

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private ObjectMapper objectMapper;

    private static final ExpressionParser PARSER = new SpelExpressionParser();
    private static final String NULL_VALUE = "NULL_VALUE";
    private static final long DEFAULT_EXPIRE = 3600L;
    private static final long LOCK_TIMEOUT = 10000L;
    private static final String LOCK_PREFIX = "lock:";

    @Around("@annotation(redisCache)")
    public Object around(ProceedingJoinPoint pjp, RedisCache redisCache) throws Throwable {
        // 1. 解析缓存key
        String key = parseKey(pjp, redisCache.key());
        log.info("【RedisCache】开始处理缓存，原始key表达式={}, 解析后key={}", redisCache.key(), key);
        
        if (!StringUtils.hasText(key) || "cache:error".equals(key) || "cache:empty".equals(key)) {
            log.warn("【RedisCache】无效的缓存key，直接执行目标方法，key={}", key);
            return pjp.proceed();
        }

        // 2. 从 Redis 中获取缓存数据
        Object cachedValue = redisUtils.get(key);
        log.info("【RedisCache】尝试获取缓存，key={}, 缓存值={}", key, cachedValue);
        
        if (cachedValue != null) {
            log.info("【RedisCache】缓存命中，key={}, value={}", key, cachedValue);
            // 自动反序列化为目标方法返回类型
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Class<?> returnType = signature.getReturnType();
            Type genericReturnType = signature.getMethod().getGenericReturnType();
            try {
                if (returnType == String.class) {
                    return cachedValue.toString();
                } else if (returnType == java.util.List.class || (cachedValue.toString().startsWith("[") && cachedValue.toString().endsWith("]"))) {
                    // 针对List类型自动反序列化
                    return objectMapper.readValue(cachedValue.toString(), objectMapper.getTypeFactory().constructType(genericReturnType));
                } else if (cachedValue instanceof String) {
                    return objectMapper.readValue((String) cachedValue, returnType);
                } else {
                    return cachedValue;
                }
            } catch (Exception e) {
                log.error("【RedisCache】自动反序列化缓存失败，key={}, type={}, error={}", key, returnType, e.getMessage());
                throw new RuntimeException("缓存反序列化失败", e);
            }
        }

        // 3. 缓存未命中，使用分布式锁
        String lockKey = LOCK_PREFIX + key.replace("cache:", "");
        String lockValue = UUID.randomUUID().toString();
        
        log.info("【RedisCache】缓存未命中，尝试获取分布式锁，lockKey={}, lockValue={}, timeout={}ms", 
                lockKey, lockValue, LOCK_TIMEOUT);
        boolean lockAcquired = redisUtils.tryLock(lockKey, lockValue, LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
        
        if (lockAcquired) {
            try {
                // 3.1 二次检查，避免缓存穿透
                cachedValue = redisUtils.get(key);
                log.info("【RedisCache】获取锁后二次检查缓存，key={}, 缓存值={}", key, cachedValue);
                
                if (cachedValue != null) {
                    log.info("【RedisCache】二次检查缓存命中，key={}, value={}", key, cachedValue);
                    return cachedValue;
                }
                
                // 3.2 执行目标方法，获取新数据
                log.info("【RedisCache】开始执行目标方法，key={}", key);
                Object newValue = pjp.proceed();
                log.info("【RedisCache】目标方法执行完成，key={}, 新值={}", key, newValue);
                
                // 3.3 将新数据存入 Redis
                if (newValue != null || redisCache.cacheNull()) {
                    redisUtils.set(key, newValue, redisCache.expire());
                    log.info("【RedisCache】更新缓存成功，key={}, value={}, expire={}", key, newValue, redisCache.expire());
                } else {
                    log.info("【RedisCache】值为null且不缓存null，key={}", key);
                }
                
                return newValue;
            } finally {
                // 3.4 释放分布式锁
                boolean unlocked = redisUtils.unlock(lockKey, lockValue);
                log.info("【RedisCache】释放分布式锁，lockKey={}, lockValue={}, result={}", lockKey, lockValue, unlocked);
            }
        } else {
            // 3.5 未获取到锁，采用自旋策略
            log.info("【RedisCache】未获取到锁，开始自旋重试，key={}", key);
            int retryCount = 0;
            int maxRetry = 3;
            long retryInterval = 100; // 重试间隔，单位毫秒
            
            while (retryCount < maxRetry) {
                cachedValue = redisUtils.get(key);
                log.info("【RedisCache】自旋重试获取缓存，key={}, 重试次数={}, 缓存值={}", key, retryCount, cachedValue);
                
                if (cachedValue != null) {
                    log.info("【RedisCache】自旋重试后缓存命中，key={}, value={}, retryCount={}", key, cachedValue, retryCount);
                    return cachedValue;
                }
                
                try {
                    Thread.sleep(retryInterval);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.warn("【RedisCache】自旋等待被中断，key={}, retryCount={}", key, retryCount);
                    throw new BusinessException(500, "缓存操作被中断，请稍后重试");
                }
                
                retryCount++;
            }
            
            // 3.6 自旋重试后仍未命中，执行目标方法
            log.info("【RedisCache】自旋重试后仍未命中，开始执行目标方法，key={}", key);
            Object newValue = pjp.proceed();
            log.info("【RedisCache】目标方法执行完成，key={}, 新值={}", key, newValue);
            
            if (newValue != null || redisCache.cacheNull()) {
                redisUtils.set(key, newValue, redisCache.expire());
                log.info("【RedisCache】更新缓存成功，key={}, value={}, expire={}", key, newValue, redisCache.expire());
            } else {
                log.info("【RedisCache】值为null且不缓存null，key={}", key);
            }
            
            return newValue;
        }
    }

    /**
     * 解析缓存key
     */
    private String parseKey(ProceedingJoinPoint pjp, String keyExpression) {
        if (!StringUtils.hasText(keyExpression)) {
            log.warn("【RedisCache】key表达式为空");
            return "cache:empty";
        }

        // 如果keyExpression不包含SpEL表达式，直接返回
        if (!keyExpression.contains("#")) {
            return "cache:" + keyExpression;
        }
        
        // 使用SpEL解析
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();
        Object[] args = pjp.getArgs();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        String[] paramNames = signature.getParameterNames();
        
        // 支持参数名和#p0两种方式
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            if (arg != null) {
                // 如果是复杂对象，转换为JSON字符串
                if (!(arg instanceof String) && !(arg instanceof Number) && !(arg instanceof Boolean)) {
                    try {
                        arg = objectMapper.writeValueAsString(arg);
                    } catch (JsonProcessingException e) {
                        log.warn("【RedisCache】参数序列化失败，使用toString()，arg={}", arg);
                        arg = String.valueOf(arg);
                    }
                }
                // 同时支持参数名和#p0两种方式
                if (paramNames != null && i < paramNames.length) {
                    context.setVariable(paramNames[i], arg);
                }
                context.setVariable("p" + i, arg);
            }
        }
        
        try {
            log.debug("【RedisCache】开始解析SpEL表达式，原始表达式={}", keyExpression);
            // 直接使用SpEL解析，不使用TemplateParserContext
            String parsedKey = parser.parseExpression(keyExpression).getValue(context, String.class);
            if (parsedKey == null) {
                log.warn("【RedisCache】SpEL表达式解析结果为空，keyExpression={}", keyExpression);
                return "cache:null";
            }
            
            // 清理和规范化key
            parsedKey = parsedKey.trim();
            // 移除多余的下划线
            parsedKey = parsedKey.replaceAll("_+", "_");
            // 移除开头和结尾的下划线
            parsedKey = parsedKey.replaceAll("^_+|_+$", "");
            // 确保key不包含特殊字符
            parsedKey = parsedKey.replaceAll("[^a-zA-Z0-9_:.-]", "_");
            
            // 如果key为空，返回默认值
            if (!StringUtils.hasText(parsedKey)) {
                log.warn("【RedisCache】SpEL表达式解析结果为空字符串，keyExpression={}", keyExpression);
                return "cache:empty";
            }
            
            log.debug("【RedisCache】SpEL表达式解析成功，keyExpression={}，解析结果={}", keyExpression, parsedKey);
            return "cache:" + parsedKey;
        } catch (Exception e) {
            log.error("【RedisCache】SpEL表达式解析失败，keyExpression={}，错误信息={}", keyExpression, e.getMessage());
            return "cache:error";
        }
    }

    /**
     * 检查条件
     */
    private boolean checkCondition(ProceedingJoinPoint point, String condition) {
        if (!StringUtils.hasText(condition)) {
            return true;
        }
        try {
            EvaluationContext context = new StandardEvaluationContext(point.getArgs());
            return Boolean.TRUE.equals(PARSER.parseExpression(condition).getValue(context, Boolean.class));
        } catch (Exception e) {
            log.error("解析条件表达式失败: {}", condition, e);
            return false;
        }
    }

    /**
     * 获取缓存值
     */
    private Object getCacheValue(String key, RedisCache redisCache) {
        try {
            if (redisCache.isHash() && StringUtils.hasText(redisCache.field())) {
                Object value = redisUtils.hGet(key, redisCache.field(), String.class);
                return parseValue(value);
            } else {
                Object value = redisUtils.get(key);
                return parseValue(value);
            }
        } catch (Exception e) {
            log.error("获取缓存值失败: {}", key, e);
            return null;
        }
    }

    /**
     * 解析缓存值
     */
    private Object parseValue(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof String && NULL_VALUE.equals(value)) {
            return null;
        }
        return value;
    }

    /**
     * 更新缓存
     */
    private void updateCache(String key, Object value, RedisCache redisCache) {
        try {
            if (value == null && !redisCache.cacheNull()) {
                return;
            }

            long expire = redisCache.expire() > 0 ? redisCache.expire() : DEFAULT_EXPIRE;
            
            // 确保key是字符串类型
            String safeKey = String.valueOf(key);
            // 确保field是字符串类型
            String safeField = StringUtils.hasText(redisCache.field()) ? String.valueOf(redisCache.field()) : null;
            // 确保value是字符串类型
            String safeValue;
            if (value == null) {
                safeValue = NULL_VALUE;
            } else if (value instanceof String) {
                safeValue = (String) value;
            } else {
                try {
                    safeValue = objectMapper.writeValueAsString(value);
                } catch (JsonProcessingException e) {
                    log.warn("【RedisCache】值序列化失败，使用toString()，value={}", value);
                    safeValue = String.valueOf(value);
                }
            }

            log.debug("【RedisCache】更新缓存，key={}, field={}, value={}, expire={}", safeKey, safeField, safeValue, expire);

            switch (redisCache.updateStrategy()) {
                case UPDATE:
                    if (redisCache.isHash() && safeField != null) {
                        redisUtils.hSet(safeKey, safeField, safeValue, expire, TimeUnit.SECONDS);
                    } else {
                        redisUtils.set(safeKey, safeValue, expire);
                    }
                    break;
                case DELETE:
                    if (redisCache.isHash() && safeField != null) {
                        redisUtils.hDelete(safeKey, safeField);
                    } else {
                        redisUtils.delete(safeKey);
                    }
                    break;
                case IGNORE:
                    log.debug("【RedisCache】忽略缓存更新，key={}", safeKey);
                    break;
            }

            // 自动续期
            if (redisCache.autoRenew() && value != null) {
                scheduleRenew(safeKey, redisCache);
            }
        } catch (Exception e) {
            log.error("【RedisCache】更新缓存失败，key={}, error={}", key, e.getMessage());
        }
    }

    /**
     * 定时续期
     */
    private void scheduleRenew(String key, RedisCache redisCache) {
        // 这里可以使用定时任务框架（如Quartz）来实现续期
        // 为了简单起见，这里只是记录日志
        log.info("缓存自动续期: {}, expire: {}", key, redisCache.expire());
    }
} 