package com.ycwl.basic.integration.common.service;

import com.ycwl.basic.integration.common.config.IntegrationProperties;
import com.ycwl.basic.utils.JacksonUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * 集成服务通用失败降级处理
 * 提供统一的降级策略，支持所有微服务集成
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class IntegrationFallbackService {
    
    private final RedisTemplate<String, String> redisTemplate;
    private final IntegrationProperties integrationProperties;
    
    // 默认降级缓存配置
    private static final String DEFAULT_FALLBACK_PREFIX = "integration:fallback:";
    private static final long DEFAULT_FALLBACK_TTL = 7; // 7天
    
    /**
     * 执行操作，失败时降级到缓存结果
     * 
     * @param serviceName 服务名称 (如: zt-device, zt-scenic)
     * @param cacheKey 缓存键
     * @param operation 主要操作
     * @param resultClass 结果类型
     * @param <T> 结果类型
     * @return 操作结果或缓存的结果
     */
    public <T> T executeWithFallback(String serviceName, String cacheKey, Supplier<T> operation, Class<T> resultClass) {
        try {
            T result = operation.get();
            if (result != null) {
                // 操作成功，保存结果用于将来的降级
                storeFallbackCache(serviceName, cacheKey, result);
            }
            return result;
        } catch (Exception e) {
            log.warn("[{}] 操作失败，尝试降级到缓存结果, cacheKey: {}", serviceName, cacheKey, e);
            T fallbackResult = getFallbackFromCache(serviceName, cacheKey, resultClass);
            if (fallbackResult == null) {
                log.error("[{}] 操作失败且无缓存数据, cacheKey: {}", serviceName, cacheKey);
                throw e;
            }
            log.info("[{}] 成功从降级缓存获取结果, cacheKey: {}", serviceName, cacheKey);
            return fallbackResult;
        }
    }
    
    /**
     * 执行操作，失败时降级到缓存结果，无返回值版本
     * 
     * @param serviceName 服务名称
     * @param cacheKey 缓存键
     * @param operation 主要操作
     */
    public void executeWithFallback(String serviceName, String cacheKey, Runnable operation) {
        try {
            operation.run();
            // 操作成功，记录成功状态
            storeFallbackCache(serviceName, cacheKey + ":success", "true");
            log.debug("[{}] 操作成功，已记录成功状态, cacheKey: {}", serviceName, cacheKey);
        } catch (Exception e) {
            log.warn("[{}] 操作失败，检查是否有历史成功记录, cacheKey: {}", serviceName, cacheKey, e);
            String successRecord = getFallbackFromCache(serviceName, cacheKey + ":success", String.class);
            if (successRecord == null) {
                log.error("[{}] 操作失败且无历史成功记录, cacheKey: {}", serviceName, cacheKey);
                throw e;
            }
            log.info("[{}] 操作失败但有历史成功记录，忽略此次失败, cacheKey: {}", serviceName, cacheKey);
        }
    }
    
    /**
     * 存储降级缓存
     */
    private void storeFallbackCache(String serviceName, String cacheKey, Object value) {
        try {
            String fullKey = buildFullCacheKey(serviceName, cacheKey);
            String jsonValue = JacksonUtil.toJSONString(value);
            long ttl = getFallbackTtl(serviceName);
            redisTemplate.opsForValue().set(fullKey, jsonValue, ttl, TimeUnit.DAYS);
            log.debug("[{}] 存储降级缓存成功, key: {}", serviceName, fullKey);
        } catch (Exception e) {
            log.warn("[{}] 存储降级缓存失败, cacheKey: {}", serviceName, cacheKey, e);
        }
    }
    
    /**
     * 从降级缓存获取结果
     */
    private <T> T getFallbackFromCache(String serviceName, String cacheKey, Class<T> resultClass) {
        try {
            String fullKey = buildFullCacheKey(serviceName, cacheKey);
            String cachedValue = redisTemplate.opsForValue().get(fullKey);
            if (cachedValue != null) {
                log.debug("[{}] 从降级缓存获取结果, key: {}", serviceName, fullKey);
                if (resultClass == String.class) {
                    return resultClass.cast(cachedValue);
                }
                return JacksonUtil.parseObject(cachedValue, resultClass);
            }
        } catch (Exception e) {
            log.warn("[{}] 从降级缓存获取结果失败, cacheKey: {}", serviceName, cacheKey, e);
        }
        return null;
    }
    
    /**
     * 清除降级缓存
     * 
     * @param serviceName 服务名称
     * @param cacheKey 缓存键
     */
    public void clearFallbackCache(String serviceName, String cacheKey) {
        String fullKey = buildFullCacheKey(serviceName, cacheKey);
        redisTemplate.delete(fullKey);
        log.debug("[{}] 清除降级缓存, key: {}", serviceName, fullKey);
    }
    
    /**
     * 批量清除服务的所有降级缓存
     * 
     * @param serviceName 服务名称
     */
    public void clearAllFallbackCache(String serviceName) {
        String pattern = buildFullCacheKey(serviceName, "*");
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
            log.info("[{}] 批量清除降级缓存，共删除 {} 个缓存项", serviceName, keys.size());
        }
    }
    
    /**
     * 检查是否有降级缓存
     * 
     * @param serviceName 服务名称
     * @param cacheKey 缓存键
     * @return 是否存在降级缓存
     */
    public boolean hasFallbackCache(String serviceName, String cacheKey) {
        String fullKey = buildFullCacheKey(serviceName, cacheKey);
        return Boolean.TRUE.equals(redisTemplate.hasKey(fullKey));
    }
    
    /**
     * 获取服务的降级缓存统计信息
     * 
     * @param serviceName 服务名称
     * @return 缓存统计信息
     */
    public FallbackCacheStats getFallbackCacheStats(String serviceName) {
        String pattern = buildFullCacheKey(serviceName, "*");
        Set<String> keys = redisTemplate.keys(pattern);
        int totalCount = keys != null ? keys.size() : 0;
        
        return FallbackCacheStats.builder()
                .serviceName(serviceName)
                .totalCacheCount(totalCount)
                .cacheKeyPattern(pattern)
                .fallbackTtlDays(getFallbackTtl(serviceName))
                .build();
    }
    
    /**
     * 构建完整的缓存键
     */
    private String buildFullCacheKey(String serviceName, String cacheKey) {
        String prefix = getFallbackPrefix(serviceName);
        return prefix + serviceName + ":" + cacheKey;
    }
    
    /**
     * 获取服务的降级缓存前缀
     */
    private String getFallbackPrefix(String serviceName) {
        if (!integrationProperties.getFallback().isEnabled()) {
            return DEFAULT_FALLBACK_PREFIX;
        }
        
        // 获取服务特定的缓存前缀
        IntegrationProperties.ServiceFallbackConfig serviceConfig = getServiceFallbackConfig(serviceName);
        if (serviceConfig != null && serviceConfig.getCachePrefix() != null) {
            return serviceConfig.getCachePrefix();
        }
        
        // 使用全局配置的前缀
        return integrationProperties.getFallback().getCachePrefix();
    }
    
    /**
     * 获取服务的降级缓存TTL
     */
    private long getFallbackTtl(String serviceName) {
        if (!integrationProperties.getFallback().isEnabled()) {
            return DEFAULT_FALLBACK_TTL;
        }
        
        // 获取服务特定的TTL
        IntegrationProperties.ServiceFallbackConfig serviceConfig = getServiceFallbackConfig(serviceName);
        if (serviceConfig != null && serviceConfig.getTtlDays() > 0) {
            return serviceConfig.getTtlDays();
        }
        
        // 使用全局配置的TTL
        return integrationProperties.getFallback().getDefaultTtlDays();
    }
    
    /**
     * 获取服务特定的降级配置
     */
    private IntegrationProperties.ServiceFallbackConfig getServiceFallbackConfig(String serviceName) {
        switch (serviceName.toLowerCase()) {
            case "zt-scenic":
                return integrationProperties.getFallback().getScenic();
            case "zt-device":
                return integrationProperties.getFallback().getDevice();
            default:
                return null;
        }
    }
    
    /**
     * 检查服务是否启用降级功能
     */
    public boolean isFallbackEnabled(String serviceName) {
        if (!integrationProperties.getFallback().isEnabled()) {
            return false;
        }
        
        IntegrationProperties.ServiceFallbackConfig serviceConfig = getServiceFallbackConfig(serviceName);
        return serviceConfig == null || serviceConfig.isEnabled();
    }
    
    /**
     * 降级缓存统计信息
     */
    @lombok.Builder
    @lombok.Data
    public static class FallbackCacheStats {
        private String serviceName;
        private int totalCacheCount;
        private String cacheKeyPattern;
        private long fallbackTtlDays;
    }
}