package com.gxwebsoft.websopy.service;

import com.alibaba.fastjson.JSONObject;
import com.gxwebsoft.websopy.entity.AppConfig;
import com.gxwebsoft.websopy.mapper.AppConfigMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * app_config（db_websopy）配置读取服务
 * 优先读 Redis 缓存，未命中跨表查 gxwebsoft_core.app_config 并写入缓存
 *
 * @author WebSoft
 * @since 2026-06-17
 */
@Slf4j
@Service
public class AppConfigService {

    @Resource
    private AppConfigMapper appConfigMapper;

    // 字段名与 bean 名保持一致：stringRedisTemplate（StringRedisTemplate bean）
    // 不能用 redisTemplate，容器中该名字对应的是 RedisTemplate<Object, Object>
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /** 缓存有效期：2 小时（与微信 access_token 一致，避免长时间 stale） */
    private static final long CACHE_TTL_HOURS = 2L;

    /** 缓存 Key 前缀，避免与 setting:* 冲突 */
    private static final String CACHE_KEY_PREFIX = "appConfig:";

    /**
     * 按 config_type 读取 app_config 全部配置项，并组装为 JSON
     *
     * @param configType 配置分类（如 wechat，对应 app_config.config_type 字段）
     * @param tenantId   租户 ID（必传）
     * @return 拼装后的 JSON；若 config_type 下无记录或发生异常，返回 null（由调用方回退到 sys_setting）
     */
    public JSONObject getByCategory(String configType, Integer tenantId) {
        if (tenantId == null) {
            log.warn("[AppConfigService] tenantId 为空，跳过 configType={}", configType);
            return null;
        }
        
        String cacheKey = buildCacheKey(configType, tenantId);

        // 1. 命中 Redis 直接返回
        String cached = stringRedisTemplate.opsForValue().get(cacheKey);
        if (cached != null && !cached.isEmpty()) {
            log.info("[AppConfigService] 命中 Redis 缓存 configType={}, tenantId={}, cacheKey={}, 缓存内容={}",
                configType, tenantId, cacheKey, cached);
            try {
                return JSONObject.parseObject(cached);
            } catch (Exception e) {
                log.warn("[AppConfigService] 缓存解析失败，删除后回源 configType={}, tenantId={}, err={}",
                    configType, tenantId, e.getMessage());
                stringRedisTemplate.delete(cacheKey);
            }
        }

        // 2. 跨表查 db_websopy.app_config
        List<AppConfig> list;
        try {
            log.info("[AppConfigService] 开始查询 app_config, configType={}, tenantId={}", configType, tenantId);
            list = appConfigMapper.selectByCategory(tenantId, configType);
            log.info("[AppConfigService] 查询结果: {} 条记录", list == null ? 0 : list.size());
            if (list != null && !list.isEmpty()) {
                for (AppConfig c : list) {
                    log.info("[AppConfigService] 配置项: configKey={}, configValue={}, configType={}, tenantId={}",
                        c.getConfigKey(), c.getConfigValue(), c.getConfigType(), c.getTenantId());
                }
            }
        } catch (Exception e) {
            log.error("[AppConfigService] 跨表查询失败 configType={}, tenantId={}, err={}",
                configType, tenantId, e.getMessage(), e);
            return null;
        }
        if (list == null || list.isEmpty()) {
            log.info("[AppConfigService] 未找到配置 configType={}, tenantId={}", configType, tenantId);
            return null;
        }

        // 3. 组装为 JSON（去掉 config_key 中的 "configType." 前缀）
        // 注意：按 config_id 降序后，相同 configKey 的后出现的会覆盖先出现的
        // 所以降序后，最新插入/更新的配置会生效
        JSONObject result = new JSONObject();
        String prefix = configType + ".";
        for (AppConfig c : list) {
            String key = c.getConfigKey();
            if (key == null) {
                continue;
            }
            if (key.startsWith(prefix)) {
                key = key.substring(prefix.length());
            }
            // 使用 putIfAbsent 保留先出现的（即 config_id 大的/最新的），
            // 但如果需要后出现的覆盖，改为 result.put(key, c.getConfigValue())
            result.putIfAbsent(key, c.getConfigValue()); // 保留第一个（最新）
            log.info("[AppConfigService] 组装JSON: key={}, value={} (tenantId={}, configId={})", 
                key, c.getConfigValue(), c.getTenantId(), c.getConfigId());
        }

        // 4. 写缓存 2 小时
        try {
            stringRedisTemplate.opsForValue().set(cacheKey, result.toJSONString(),
                CACHE_TTL_HOURS, TimeUnit.HOURS);
        } catch (Exception e) {
            log.warn("[AppConfigService] 写缓存失败（不影响返回） err={}", e.getMessage());
        }

        log.info("[AppConfigService] 从 app_config 读取配置 configType={}, tenantId={}, fields={}",
            configType, tenantId, result.size());
        return result;
    }

    /**
     * 主动失效缓存（供配置变更时调用）
     */
    public void evict(String configType, Integer tenantId) {
        if (tenantId == null) {
            return;
        }
        stringRedisTemplate.delete(buildCacheKey(configType, tenantId));
    }

    private String buildCacheKey(String configType, Integer tenantId) {
        return CACHE_KEY_PREFIX + tenantId + ":" + configType;
    }
}
