package com.ycwl.basic.repository;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.ycwl.basic.mapper.WechatSubscribeEventTemplateMapper;
import com.ycwl.basic.mapper.WechatSubscribeSceneTemplateMapper;
import com.ycwl.basic.mapper.WechatSubscribeTemplateConfigMapper;
import com.ycwl.basic.model.mobile.notify.resp.WechatSubscribeAllScenesResp;
import com.ycwl.basic.model.pc.notify.entity.WechatSubscribeEventTemplateEntity;
import com.ycwl.basic.model.pc.notify.entity.WechatSubscribeSceneTemplateEntity;
import com.ycwl.basic.model.pc.notify.entity.WechatSubscribeTemplateConfigEntity;
import com.ycwl.basic.utils.JacksonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 微信订阅消息配置仓库（scenic 覆盖：scenic_id=具体值 > scenic_id=0）
 * 支持 Redis 缓存，减少数据库查询
 *
 * @Author: System
 * @Date: 2025/12/31
 */
@Slf4j
@Component
public class WechatSubscribeNotifyConfigRepository {

    private static final long DEFAULT_SCENIC_ID = 0L;

    /**
     * 场景模板配置缓存KEY
     */
    private static final String SCENE_TEMPLATE_CONFIGS_KEY = "wechat:subscribe:scene:configs:%s:%s";

    /**
     * 事件模板配置缓存KEY
     */
    private static final String EVENT_TEMPLATE_CONFIGS_KEY = "wechat:subscribe:event:configs:%s:%s";

    /**
     * 景区所有场景缓存KEY前缀（用于批量清除）
     */
    private static final String SCENE_TEMPLATE_SCENIC_PREFIX = "wechat:subscribe:scene:configs:%s:*";

    /**
     * 景区所有事件缓存KEY前缀（用于批量清除）
     */
    private static final String EVENT_TEMPLATE_SCENIC_PREFIX = "wechat:subscribe:event:configs:%s:*";

    /**
     * 景区所有场景及模板缓存KEY
     */
    private static final String ALL_SCENES_TEMPLATES_KEY = "wechat:subscribe:all-scenes:configs:%s";

    /**
     * 缓存过期时间（小时）
     */
    private static final long CACHE_EXPIRE_HOURS = 1;

    private final WechatSubscribeTemplateConfigMapper templateConfigMapper;
    private final WechatSubscribeSceneTemplateMapper sceneTemplateMapper;
    private final WechatSubscribeEventTemplateMapper eventTemplateMapper;
    private final RedisTemplate<String, String> redisTemplate;

    public WechatSubscribeNotifyConfigRepository(WechatSubscribeTemplateConfigMapper templateConfigMapper,
                                                 WechatSubscribeSceneTemplateMapper sceneTemplateMapper,
                                                 WechatSubscribeEventTemplateMapper eventTemplateMapper,
                                                 RedisTemplate<String, String> redisTemplate) {
        this.templateConfigMapper = templateConfigMapper;
        this.sceneTemplateMapper = sceneTemplateMapper;
        this.eventTemplateMapper = eventTemplateMapper;
        this.redisTemplate = redisTemplate;
    }

    public List<WechatSubscribeSceneTemplateEntity> listEffectiveSceneTemplateMappings(Long scenicId, String sceneKey) {
        Objects.requireNonNull(scenicId, "scenicId is null");
        if (StringUtils.isBlank(sceneKey)) {
            throw new IllegalArgumentException("sceneKey is blank");
        }

        List<Long> scenicIds = List.of(DEFAULT_SCENIC_ID, scenicId);
        QueryWrapper<WechatSubscribeSceneTemplateEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("scene_key", sceneKey)
                .in("scenic_id", scenicIds);
        List<WechatSubscribeSceneTemplateEntity> rows = sceneTemplateMapper.selectList(wrapper);
        return pickEffectiveByTemplateKey(rows, scenicId).values().stream()
                .filter(this::isEnabled)
                .sorted((a, b) -> {
                    int cmp = Integer.compare(safeInt(a.getSortOrder()), safeInt(b.getSortOrder()));
                    if (cmp != 0) {
                        return cmp;
                    }
                    return Objects.toString(a.getTemplateKey(), "").compareTo(Objects.toString(b.getTemplateKey(), ""));
                })
                .collect(Collectors.toList());
    }

    public List<WechatSubscribeEventTemplateEntity> listEffectiveEventTemplateMappings(Long scenicId, String eventKey) {
        Objects.requireNonNull(scenicId, "scenicId is null");
        if (StringUtils.isBlank(eventKey)) {
            throw new IllegalArgumentException("eventKey is blank");
        }

        List<Long> scenicIds = List.of(DEFAULT_SCENIC_ID, scenicId);
        QueryWrapper<WechatSubscribeEventTemplateEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("event_key", eventKey)
                .in("scenic_id", scenicIds);
        List<WechatSubscribeEventTemplateEntity> rows = eventTemplateMapper.selectList(wrapper);
        return pickEffectiveByTemplateKey(rows, scenicId).values().stream()
                .filter(this::isEnabled)
                .sorted((a, b) -> {
                    int cmp = Integer.compare(safeInt(a.getSortOrder()), safeInt(b.getSortOrder()));
                    if (cmp != 0) {
                        return cmp;
                    }
                    return Objects.toString(a.getTemplateKey(), "").compareTo(Objects.toString(b.getTemplateKey(), ""));
                })
                .collect(Collectors.toList());
    }

    public Map<String, WechatSubscribeTemplateConfigEntity> getEffectiveTemplateConfigs(Long scenicId,
                                                                                        Collection<String> templateKeys) {
        Objects.requireNonNull(scenicId, "scenicId is null");
        if (CollectionUtils.isEmpty(templateKeys)) {
            return new HashMap<>();
        }

        List<Long> scenicIds = List.of(DEFAULT_SCENIC_ID, scenicId);
        QueryWrapper<WechatSubscribeTemplateConfigEntity> wrapper = new QueryWrapper<>();
        wrapper.in("template_key", templateKeys)
                .in("scenic_id", scenicIds);
        List<WechatSubscribeTemplateConfigEntity> rows = templateConfigMapper.selectList(wrapper);
        Map<String, WechatSubscribeTemplateConfigEntity> effective = new HashMap<>();
        for (WechatSubscribeTemplateConfigEntity row : rows) {
            if (row == null || row.getTemplateKey() == null) {
                continue;
            }
            WechatSubscribeTemplateConfigEntity existing = effective.get(row.getTemplateKey());
            if (existing == null) {
                effective.put(row.getTemplateKey(), row);
                continue;
            }
            if (Objects.equals(row.getScenicId(), scenicId)) {
                effective.put(row.getTemplateKey(), row);
            }
        }
        return effective;
    }

    private boolean isEnabled(WechatSubscribeSceneTemplateEntity entity) {
        return entity != null && Objects.equals(entity.getEnabled(), 1);
    }

    private boolean isEnabled(WechatSubscribeEventTemplateEntity entity) {
        return entity != null && Objects.equals(entity.getEnabled(), 1);
    }

    private static int safeInt(Integer value) {
        return value != null ? value : 0;
    }

    private static <T> Map<String, T> pickEffectiveByTemplateKey(List<T> rows, Long scenicId) {
        Map<String, T> effective = new HashMap<>();
        if (rows == null || rows.isEmpty()) {
            return effective;
        }
        for (T row : rows) {
            String templateKey = getTemplateKey(row);
            if (templateKey == null) {
                continue;
            }
            T existing = effective.get(templateKey);
            if (existing == null) {
                effective.put(templateKey, row);
                continue;
            }
            Long rowScenicId = getScenicId(row);
            if (Objects.equals(rowScenicId, scenicId)) {
                effective.put(templateKey, row);
            }
        }
        return effective;
    }

    private static String getTemplateKey(Object row) {
        if (row instanceof WechatSubscribeSceneTemplateEntity entity) {
            return entity.getTemplateKey();
        }
        if (row instanceof WechatSubscribeEventTemplateEntity entity) {
            return entity.getTemplateKey();
        }
        return null;
    }

    private static Long getScenicId(Object row) {
        if (row instanceof WechatSubscribeSceneTemplateEntity entity) {
            return entity.getScenicId();
        }
        if (row instanceof WechatSubscribeEventTemplateEntity entity) {
            return entity.getScenicId();
        }
        return null;
    }

    /**
     * 获取景区下所有启用的场景Key列表（去重）
     * 包含默认配置（scenicId=0）和景区特定配置
     *
     * @param scenicId 景区ID
     * @return 去重后的场景Key列表
     */
    public List<String> listAllSceneKeys(Long scenicId) {
        Objects.requireNonNull(scenicId, "scenicId is null");

        List<Long> scenicIds = List.of(DEFAULT_SCENIC_ID, scenicId);
        QueryWrapper<WechatSubscribeSceneTemplateEntity> wrapper = new QueryWrapper<>();
        wrapper.in("scenic_id", scenicIds)
                .eq("enabled", 1)
                .select("DISTINCT scene_key");
        List<WechatSubscribeSceneTemplateEntity> rows = sceneTemplateMapper.selectList(wrapper);
        return rows.stream()
                .map(WechatSubscribeSceneTemplateEntity::getSceneKey)
                .filter(Objects::nonNull)
                .distinct()
                .sorted()
                .collect(Collectors.toList());
    }

    /**
     * 获取景区下所有场景及其模板列表（带缓存，不含用户授权信息）
     *
     * @param scenicId 景区ID
     * @return 所有场景及模板配置
     */
    public WechatSubscribeAllScenesResp getAllScenesWithTemplatesCached(Long scenicId) {
        Objects.requireNonNull(scenicId, "scenicId is null");

        String cacheKey = String.format(ALL_SCENES_TEMPLATES_KEY, scenicId);

        // 1. 尝试从缓存读取
        Boolean hasKey = redisTemplate.hasKey(cacheKey);
        if (Boolean.TRUE.equals(hasKey)) {
            String cacheValue = redisTemplate.opsForValue().get(cacheKey);
            if (cacheValue != null) {
                log.debug("从缓存读取所有场景模板配置: scenicId={}", scenicId);
                return JacksonUtil.parseObject(cacheValue, WechatSubscribeAllScenesResp.class);
            }
        }

        // 2. 从数据库加载
        WechatSubscribeAllScenesResp resp = loadAllScenesWithTemplates(scenicId);

        // 3. 写入缓存
        String json = JacksonUtil.toJSONString(resp);
        redisTemplate.opsForValue().set(cacheKey, json, CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
        log.debug("所有场景模板配置缓存写入: scenicId={}, sceneCount={}",
                scenicId, resp.getScenes() != null ? resp.getScenes().size() : 0);

        return resp;
    }

    /**
     * 加载所有场景及其模板配置（内部方法）
     */
    private WechatSubscribeAllScenesResp loadAllScenesWithTemplates(Long scenicId) {
        List<String> sceneKeys = listAllSceneKeys(scenicId);

        WechatSubscribeAllScenesResp resp = new WechatSubscribeAllScenesResp();
        resp.setScenicId(scenicId);

        List<WechatSubscribeAllScenesResp.SceneWithTemplates> scenes = new ArrayList<>();
        for (String sceneKey : sceneKeys) {
            List<WechatSubscribeTemplateConfigEntity> configs = getSceneTemplateConfigsCached(scenicId, sceneKey);

            WechatSubscribeAllScenesResp.SceneWithTemplates sceneWithTemplates = new WechatSubscribeAllScenesResp.SceneWithTemplates();
            sceneWithTemplates.setSceneKey(sceneKey);

            List<WechatSubscribeAllScenesResp.StaticTemplateInfo> templates = new ArrayList<>();
            for (WechatSubscribeTemplateConfigEntity cfg : configs) {
                if (cfg == null || StringUtils.isBlank(cfg.getWechatTemplateId())) {
                    continue;
                }
                WechatSubscribeAllScenesResp.StaticTemplateInfo info = new WechatSubscribeAllScenesResp.StaticTemplateInfo();
                info.setTemplateKey(cfg.getTemplateKey());
                info.setWechatTemplateId(cfg.getWechatTemplateId());
                info.setTitle(StringUtils.isNotBlank(cfg.getTitleTemplate()) ? cfg.getTitleTemplate() : cfg.getTemplateKey());
                info.setDescription(cfg.getDescription());
                templates.add(info);
            }
            sceneWithTemplates.setTemplates(templates);
            scenes.add(sceneWithTemplates);
        }
        resp.setScenes(scenes);
        return resp;
    }

    // ==================== 带缓存的配置查询方法 ====================

    /**
     * 获取场景模板配置列表（带缓存）
     * 缓存最终的配置列表，避免重复查询和处理覆盖逻辑
     *
     * @param scenicId 景区ID
     * @param sceneKey 场景标识
     * @return 启用的模板配置列表
     */
    public List<WechatSubscribeTemplateConfigEntity> getSceneTemplateConfigsCached(Long scenicId, String sceneKey) {
        Objects.requireNonNull(scenicId, "scenicId is null");
        if (StringUtils.isBlank(sceneKey)) {
            throw new IllegalArgumentException("sceneKey is blank");
        }

        String cacheKey = String.format(SCENE_TEMPLATE_CONFIGS_KEY, scenicId, sceneKey);

        // 1. 尝试从缓存读取
        Boolean hasKey = redisTemplate.hasKey(cacheKey);
        if (Boolean.TRUE.equals(hasKey)) {
            String cacheValue = redisTemplate.opsForValue().get(cacheKey);
            if (cacheValue != null) {
                log.debug("从缓存读取场景模板配置: scenicId={}, sceneKey={}", scenicId, sceneKey);
                return JacksonUtil.parseObject(cacheValue, new TypeReference<List<WechatSubscribeTemplateConfigEntity>>() {});
            }
        }

        // 2. 从数据库查询
        List<WechatSubscribeTemplateConfigEntity> configs = loadSceneTemplateConfigs(scenicId, sceneKey);

        // 3. 写入缓存
        String json = JacksonUtil.toJSONString(configs);
        redisTemplate.opsForValue().set(cacheKey, json, CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
        log.debug("场景模板配置缓存写入: scenicId={}, sceneKey={}, count={}", scenicId, sceneKey, configs.size());

        return configs;
    }

    /**
     * 获取事件模板配置列表（带缓存）
     *
     * @param scenicId 景区ID
     * @param eventKey 事件标识
     * @return 启用的模板配置列表
     */
    public List<WechatSubscribeTemplateConfigEntity> getEventTemplateConfigsCached(Long scenicId, String eventKey) {
        Objects.requireNonNull(scenicId, "scenicId is null");
        if (StringUtils.isBlank(eventKey)) {
            throw new IllegalArgumentException("eventKey is blank");
        }

        String cacheKey = String.format(EVENT_TEMPLATE_CONFIGS_KEY, scenicId, eventKey);

        // 1. 尝试从缓存读取
        Boolean hasKey = redisTemplate.hasKey(cacheKey);
        if (Boolean.TRUE.equals(hasKey)) {
            String cacheValue = redisTemplate.opsForValue().get(cacheKey);
            if (cacheValue != null) {
                log.debug("从缓存读取事件模板配置: scenicId={}, eventKey={}", scenicId, eventKey);
                return JacksonUtil.parseObject(cacheValue, new TypeReference<List<WechatSubscribeTemplateConfigEntity>>() {});
            }
        }

        // 2. 从数据库查询
        List<WechatSubscribeTemplateConfigEntity> configs = loadEventTemplateConfigs(scenicId, eventKey);

        // 3. 写入缓存
        String json = JacksonUtil.toJSONString(configs);
        redisTemplate.opsForValue().set(cacheKey, json, CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
        log.debug("事件模板配置缓存写入: scenicId={}, eventKey={}, count={}", scenicId, eventKey, configs.size());

        return configs;
    }

    /**
     * 加载场景模板配置（内部方法，处理覆盖逻辑）
     */
    private List<WechatSubscribeTemplateConfigEntity> loadSceneTemplateConfigs(Long scenicId, String sceneKey) {
        List<WechatSubscribeSceneTemplateEntity> mappings = listEffectiveSceneTemplateMappings(scenicId, sceneKey);
        if (CollectionUtils.isEmpty(mappings)) {
            return new ArrayList<>();
        }

        Set<String> templateKeys = mappings.stream()
                .map(WechatSubscribeSceneTemplateEntity::getTemplateKey)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<String, WechatSubscribeTemplateConfigEntity> configMap = getEffectiveTemplateConfigs(scenicId, templateKeys);

        List<WechatSubscribeTemplateConfigEntity> result = new ArrayList<>();
        for (WechatSubscribeSceneTemplateEntity mapping : mappings) {
            WechatSubscribeTemplateConfigEntity cfg = configMap.get(mapping.getTemplateKey());
            if (cfg == null || !Objects.equals(cfg.getEnabled(), 1)) {
                continue;
            }
            result.add(cfg);
        }
        return result;
    }

    /**
     * 加载事件模板配置（内部方法，处理覆盖逻辑）
     */
    private List<WechatSubscribeTemplateConfigEntity> loadEventTemplateConfigs(Long scenicId, String eventKey) {
        List<WechatSubscribeEventTemplateEntity> mappings = listEffectiveEventTemplateMappings(scenicId, eventKey);
        if (CollectionUtils.isEmpty(mappings)) {
            return new ArrayList<>();
        }

        Set<String> templateKeys = mappings.stream()
                .map(WechatSubscribeEventTemplateEntity::getTemplateKey)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<String, WechatSubscribeTemplateConfigEntity> configMap = getEffectiveTemplateConfigs(scenicId, templateKeys);

        List<WechatSubscribeTemplateConfigEntity> result = new ArrayList<>();
        for (WechatSubscribeEventTemplateEntity mapping : mappings) {
            WechatSubscribeTemplateConfigEntity cfg = configMap.get(mapping.getTemplateKey());
            if (cfg == null || !Objects.equals(cfg.getEnabled(), 1)) {
                continue;
            }
            // 复制去重配置到模板实体中
            cfg.setDedupSeconds(mapping.getDedupSeconds());
            result.add(cfg);
        }
        return result;
    }

    // ==================== 缓存清除方法 ====================

    /**
     * 清除指定场景的模板配置缓存
     *
     * @param scenicId 景区ID
     * @param sceneKey 场景标识
     */
    public void clearSceneTemplateConfigsCache(Long scenicId, String sceneKey) {
        if (scenicId == null || StringUtils.isBlank(sceneKey)) {
            return;
        }
        String cacheKey = String.format(SCENE_TEMPLATE_CONFIGS_KEY, scenicId, sceneKey);
        redisTemplate.delete(cacheKey);
        log.debug("清除场景模板配置缓存: scenicId={}, sceneKey={}", scenicId, sceneKey);
    }

    /**
     * 清除指定事件的模板配置缓存
     *
     * @param scenicId 景区ID
     * @param eventKey 事件标识
     */
    public void clearEventTemplateConfigsCache(Long scenicId, String eventKey) {
        if (scenicId == null || StringUtils.isBlank(eventKey)) {
            return;
        }
        String cacheKey = String.format(EVENT_TEMPLATE_CONFIGS_KEY, scenicId, eventKey);
        redisTemplate.delete(cacheKey);
        log.debug("清除事件模板配置缓存: scenicId={}, eventKey={}", scenicId, eventKey);
    }

    /**
     * 清除景区下所有订阅消息配置缓存
     * 在景区配置变更时调用
     *
     * @param scenicId 景区ID
     */
    public void clearAllConfigsCacheByScenic(Long scenicId) {
        if (scenicId == null) {
            return;
        }

        // 清除场景配置缓存
        String scenePattern = String.format(SCENE_TEMPLATE_SCENIC_PREFIX, scenicId);
        deleteByPattern(scenePattern);

        // 清除事件配置缓存
        String eventPattern = String.format(EVENT_TEMPLATE_SCENIC_PREFIX, scenicId);
        deleteByPattern(eventPattern);

        // 清除所有场景模板缓存
        String allScenesKey = String.format(ALL_SCENES_TEMPLATES_KEY, scenicId);
        redisTemplate.delete(allScenesKey);

        log.info("清除景区所有订阅消息配置缓存: scenicId={}", scenicId);
    }

    /**
     * 清除所有订阅消息配置缓存
     * 在全局配置变更时调用（如默认配置修改）
     */
    public void clearAllConfigsCache() {
        deleteByPattern("wechat:subscribe:scene:configs:*");
        deleteByPattern("wechat:subscribe:event:configs:*");
        deleteByPattern("wechat:subscribe:all-scenes:configs:*");
        log.warn("清除所有订阅消息配置缓存");
    }

    /**
     * 根据模式删除缓存
     */
    private void deleteByPattern(String pattern) {
        try {
            var keys = redisTemplate.keys(pattern);
            if (keys != null && !keys.isEmpty()) {
                redisTemplate.delete(keys);
                log.debug("删除缓存: pattern={}, count={}", pattern, keys.size());
            }
        } catch (Exception e) {
            log.error("删除缓存失败: pattern={}", pattern, e);
        }
    }
}
