package com.ycwl.basic.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ycwl.basic.pricing.dto.PriceCalculationResult;
import com.ycwl.basic.pricing.dto.ProductItem;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.List;

/**
 * 价格缓存服务
 * 使用 Redis 实现价格查询结果的缓存，缓存时间为5分钟
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PriceCacheService {
    
    private final StringRedisTemplate redisTemplate;
    private final ObjectMapper objectMapper;
    
    // 缓存Key前缀
    private static final String CACHE_PREFIX = "pricing:cache:";
    
    // 缓存过期时间：5分钟
    private static final Duration CACHE_DURATION = Duration.ofMinutes(10);
    
    /**
     * 生成缓存键
     * 格式：pricing:cache:{userId}:{scenicId}:{productsHash}
     * 
     * @param userId 用户ID
     * @param scenicId 景区ID
     * @param products 商品列表
     * @return 缓存键
     */
    public String generateCacheKey(Long userId, Long scenicId, List<ProductItem> products) {
        String productsHash = calculateProductsHash(products);
        return CACHE_PREFIX + userId + ":" + scenicId + ":" + productsHash;
    }
    
    /**
     * 计算商品列表的哈希值
     * 基于商品类型、ID、购买数量等必传字段生成唯一哈希，忽略可选的quantity字段
     * 
     * @param products 商品列表
     * @return 哈希值
     */
    private String calculateProductsHash(List<ProductItem> products) {
        try {
            // 构建缓存用的商品信息字符串，只包含必传字段
            StringBuilder cacheInfo = new StringBuilder();
            for (ProductItem product : products) {
                cacheInfo.append(product.getProductType()).append(":")
                        .append(product.getProductId()).append(":")
                        .append(product.getPurchaseCount() != null ? product.getPurchaseCount() : 1).append(":")
                        .append(product.getScenicId()).append(";");
            }
            
            // 计算SHA-256哈希
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(cacheInfo.toString().getBytes());
            
            // 转换为16进制字符串
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            
            // 返回前16位作为哈希值（足够避免冲突）
            return hexString.substring(0, 16);
            
        } catch (NoSuchAlgorithmException e) {
            log.error("计算商品列表哈希值失败", e);
            // 降级策略：使用关键字段的hashCode
            StringBuilder fallback = new StringBuilder();
            for (ProductItem product : products) {
                fallback.append(product.getProductType()).append(":")
                        .append(product.getProductId()).append(":")
                        .append(product.getPurchaseCount() != null ? product.getPurchaseCount() : 1).append(";");
            }
            return String.valueOf(fallback.toString().hashCode());
        }
    }
    
    /**
     * 缓存价格计算结果
     * 
     * @param userId 用户ID
     * @param scenicId 景区ID
     * @param products 商品列表
     * @param result 价格计算结果
     * @return 缓存键
     */
    public String cachePriceResult(Long userId, Long scenicId, List<ProductItem> products, PriceCalculationResult result) {
        String cacheKey = generateCacheKey(userId, scenicId, products);
        
        try {
            String resultJson = objectMapper.writeValueAsString(result);
            redisTemplate.opsForValue().set(cacheKey, resultJson, CACHE_DURATION);
            
            log.info("价格计算结果已缓存: cacheKey={}, userId={}, scenicId={}", 
                    cacheKey, userId, scenicId);
            
            return cacheKey;
            
        } catch (JsonProcessingException e) {
            log.error("缓存价格计算结果失败: userId={}, scenicId={}", userId, scenicId, e);
            return null;
        }
    }
    
    /**
     * 获取缓存的价格计算结果
     * 
     * @param userId 用户ID
     * @param scenicId 景区ID
     * @param products 商品列表
     * @return 缓存的价格计算结果，如果不存在则返回null
     */
    public PriceCalculationResult getCachedPriceResult(Long userId, Long scenicId, List<ProductItem> products) {
        String cacheKey = generateCacheKey(userId, scenicId, products);
        return getCachedPriceResult(cacheKey);
    }
    
    /**
     * 根据缓存键获取价格计算结果
     * 
     * @param cacheKey 缓存键
     * @return 缓存的价格计算结果，如果不存在则返回null
     */
    public PriceCalculationResult getCachedPriceResult(String cacheKey) {
        try {
            String resultJson = redisTemplate.opsForValue().get(cacheKey);
            if (resultJson == null) {
                log.debug("价格缓存不存在: cacheKey={}", cacheKey);
                return null;
            }
            
            PriceCalculationResult result = objectMapper.readValue(resultJson, PriceCalculationResult.class);
            log.info("获取到缓存的价格计算结果: cacheKey={}", cacheKey);
            
            return result;
            
        } catch (JsonProcessingException e) {
            log.error("解析缓存的价格计算结果失败: cacheKey={}", cacheKey, e);
            return null;
        }
    }
    
    /**
     * 检查价格缓存是否存在
     * 
     * @param userId 用户ID
     * @param scenicId 景区ID
     * @param products 商品列表
     * @return true如果缓存存在且未过期
     */
    public boolean isCacheExists(Long userId, Long scenicId, List<ProductItem> products) {
        String cacheKey = generateCacheKey(userId, scenicId, products);
        return Boolean.TRUE.equals(redisTemplate.hasKey(cacheKey));
    }
    
    /**
     * 验证并消费价格缓存（一次性使用）
     * 查询缓存结果后立即删除，确保缓存只能被使用一次
     * 
     * @param userId 用户ID
     * @param scenicId 景区ID  
     * @param products 商品列表
     * @return 缓存的价格计算结果，如果不存在则返回null
     */
    public PriceCalculationResult validateAndConsumePriceCache(Long userId, Long scenicId, List<ProductItem> products) {
        String cacheKey = generateCacheKey(userId, scenicId, products);
        
        try {
            // 使用 Redis 的 GETDEL 命令原子性地获取并删除缓存
            String resultJson = redisTemplate.opsForValue().getAndDelete(cacheKey);
            
            if (resultJson == null) {
                log.warn("价格缓存不存在或已过期: userId={}, scenicId={}, cacheKey={}", 
                        userId, scenicId, cacheKey);
                return null;
            }
            
            PriceCalculationResult result = objectMapper.readValue(resultJson, PriceCalculationResult.class);
            log.info("价格缓存验证成功并已消费: userId={}, scenicId={}, cacheKey={}", 
                    userId, scenicId, cacheKey);
            
            return result;
            
        } catch (JsonProcessingException e) {
            log.error("解析价格缓存失败: userId={}, scenicId={}, cacheKey={}", 
                    userId, scenicId, cacheKey, e);
            // 缓存解析失败时也要删除无效缓存
            redisTemplate.delete(cacheKey);
            return null;
        }
    }
    
    /**
     * 删除价格缓存
     * 
     * @param userId 用户ID
     * @param scenicId 景区ID
     * @param products 商品列表
     * @return true如果删除成功
     */
    public boolean deletePriceCache(Long userId, Long scenicId, List<ProductItem> products) {
        String cacheKey = generateCacheKey(userId, scenicId, products);
        return Boolean.TRUE.equals(redisTemplate.delete(cacheKey));
    }
    
    /**
     * 获取缓存剩余过期时间
     * 
     * @param userId 用户ID
     * @param scenicId 景区ID
     * @param products 商品列表
     * @return 剩余时间（秒），-1表示永不过期，-2表示不存在
     */
    public long getCacheExpireTime(Long userId, Long scenicId, List<ProductItem> products) {
        String cacheKey = generateCacheKey(userId, scenicId, products);
        Long expire = redisTemplate.getExpire(cacheKey);
        return expire != null ? expire : -2;
    }
}