package com.centreproject.system.service.job.factory;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.centreproject.common.utils.DateUtils;
import com.centreproject.system.domain.sync.kpi.AlertRecord;
import com.centreproject.system.domain.sync.kpi.SyncRule;
import com.centreproject.system.mapper.sync.kpi.AlertRecordMapper;
import com.centreproject.system.mapper.sync.kpi.SyncRuleMapper;
import com.centreproject.system.service.job.context.SyncRuleContext;
import com.centreproject.system.service.job.context.TypeConverter;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 同步规则处理器工厂（支持多模块、动态类型转换）
 *
 * todo 发送邮件后续处理
 * @author zhonglei.xu
 */
@Component
public class SyncRuleHandlerFactory {
    private static final Logger log = LoggerFactory.getLogger(SyncRuleHandlerFactory.class);

    @Resource
    private SyncRuleMapper syncRuleMapper;
    @Resource
    private AlertRecordMapper alertRecordMapper;
    // 注入类型转换器工厂
    @Resource
    private TypeConverterFactory converterFactory;

    // 缓存所有模块的规则上下文（key=模块名，value=该模块的规则上下文）
    private final Map<String, SyncRuleContext> moduleContextCache = new ConcurrentHashMap<>();

    /**
     * 获取指定模块的规则上下文（懒加载：首次调用时加载规则，后续直接返回缓存）
     * @param moduleName 模块名（如 "park"）
     * @return 该模块的规则上下文（含缓存的规则）
     */
    public SyncRuleContext getContext(String moduleName) {
        // 1. 先查缓存，命中直接返回
        if (moduleContextCache.containsKey(moduleName)) {
            return moduleContextCache.get(moduleName);
        }

        // 2. 缓存未命中，查询数据库加载规则（加锁避免并发重复加载）
        synchronized (this) {
            // 双重检查锁
            if (moduleContextCache.containsKey(moduleName)) {
                return moduleContextCache.get(moduleName);
            }

            // 3. 查询该模块所有启用的规则
            QueryWrapper<SyncRule> wrapper = new QueryWrapper<>();
            wrapper.eq("module_name", moduleName)
                    // 只加载启用的规则
                    .eq("status", 1);
            List<SyncRule> rules = syncRuleMapper.selectList(wrapper);

            // 4. 构建规则缓存（按字段名分组）
            Map<String, SyncRule> ruleCache = rules.stream()
                    .collect(Collectors.toMap(
                            SyncRule::getFieldName,
                            Function.identity(),
                            // 字段名重复时保留第一条规则
                            (existing, replacement) -> existing
                    ));

            // 5. 创建上下文并缓存
            SyncRuleContext context = new SyncRuleContext();
            context.setModuleName(moduleName);
            context.setRuleCache(ruleCache);
            moduleContextCache.put(moduleName, context);

            log.info("模块[{}]规则上下文初始化完成，加载规则{}条", moduleName, ruleCache.size());
            return context;
        }
    }

    /**
     * 应用空值默认值规则（根据目标类型动态转换）
     * @param moduleName 模块名（如 "park"）
     * @param fieldName 字段名（如 "attendance"）
     * @param value 当前值（可能为 null）
     * @param targetType 目标字段的类型（如 Integer.class、BigDecimal.class）
     * @return 转换后的默认值（类型为 targetType），若无需转换则返回原 value
     */
    @Transactional
    public <T> T applyDefaultValue(String moduleName, String fieldName, Object value, Class<T> targetType) {
        // 1. 若当前值不为空，直接返回（无需应用默认值规则）
        if (value != null) {
            try {
                // 强转为目标类型（如 Integer -> String）
                return targetType.cast(value);
            } catch (ClassCastException e) {
                log.error("字段[{}]值[{}]类型转换失败（目标类型：{}）",
                        fieldName, value, targetType.getSimpleName(), e);
                throw new IllegalArgumentException("值类型与目标类型不匹配", e);
            }
        }

        // 2. 获取该字段的空值规则
        SyncRuleContext context = getContext(moduleName);
        SyncRule rule = context.getRuleCache().get(fieldName);
        // 非空值规则，返回 null
        if (rule == null || rule.getRuleType() != 1) {
            log.debug("模块[{}]字段[{}]无启用的空值规则，返回 null", moduleName, fieldName);
            return null;
        }

        // 3. 获取字符串默认值
        String defaultValue = rule.getDefaultValue();
        if (defaultValue == null) {
            log.debug("模块[{}]字段[{}]空值规则默认值为 null", moduleName, fieldName);
            return null;
        }

        // 4. 根据目标类型动态转换（核心：通过转换器工厂选择转换器）
        try {
            TypeConverter<T> converter = converterFactory.getConverter(targetType);
            T convertedValue = converter.convert(defaultValue);
            log.debug("模块[{}]字段[{}]应用空值规则：默认值[{}] -> 转换为[{}]（类型：{}）",
                    moduleName, fieldName, defaultValue, convertedValue, targetType.getSimpleName());
            return convertedValue;
        } catch (NumberFormatException e) {
            log.error("模块[{}]字段[{}]默认值[{}]转换为类型[{}]失败",
                    moduleName, fieldName, defaultValue, targetType.getSimpleName(), e);
            throw new IllegalArgumentException(
                    String.format("默认值转换失败：%s -> %s", defaultValue, targetType.getSimpleName()), e);
        }
    }

    /**
     * 应用阈值告警规则（通用方法，支持任意模块）
     * @param moduleName 模块名
     * @param fieldName 字段名
     * @param value 当前值
     */
    public void checkThreshold(String moduleName, String fieldName, Object value) {
        if (value == null) {
            return;
        }

        SyncRuleContext context = getContext(moduleName);
        SyncRule rule = context.getRuleCache().get(fieldName);
        // 非阈值规则，不处理
        if (rule == null || rule.getRuleType() != 2) {
            return;
        }

        // 检查阈值并触发告警（日志/邮件/消息队列）
        checkAndAlert(moduleName, fieldName, value, rule);
    }

    /**
     * 检查阈值并触发告警
     */
    private void checkAndAlert(String moduleName, String fieldName, Object value, SyncRule rule) {
        try {
            BigDecimal val = new BigDecimal(value.toString());
            BigDecimal min = rule.getThresholdMin();
            BigDecimal max = rule.getThresholdMax();

            boolean exceedsMin = min != null && val.compareTo(min) < 0;
            boolean exceedsMax = max != null && val.compareTo(max) > 0;

            if (exceedsMin || exceedsMax) {
                String alertMsg = String.format(
                        "模块[%s]字段[%s]值[%s]超出阈值！min=%s, max=%s",
                        moduleName, fieldName, val, min, max
                );
                log.warn(alertMsg);
                // 先存入表中
                Date todayMidnight = Date.from(
                        LocalDate.now()
                                .atStartOfDay()
                                .atZone(ZoneId.of("Asia/Riyadh"))
                                .toInstant()
                );
                AlertRecord alertRecord = new AlertRecord().setModuleName(moduleName)
                        .setFieldName(fieldName)
                        .setThresholdMin(min)
                        .setThresholdMax(max)
                        .setCurrentValue(val)
                        .setAlertStatus(0)
                        .setAlertTime(todayMidnight)
                        .setAlertContent(alertMsg)
                        .setAlertEmails(rule.getAlertEmail());
                alertRecordMapper.batchInsertOrUpdate(alertRecord);
                // TODO: 集成邮件发送、消息队列等告警通道（调用你的告警服务）
            }
        } catch (NumberFormatException e) {
            log.error("模块[{}]字段[{}]值[{}]不是数字，无法检查阈值", moduleName, fieldName, value);
        }
    }

    /**
     * 手动刷新模块规则缓存（支持动态更新规则后刷新）
     * @param moduleName 模块名（如为null则刷新所有模块）
     */
    public void refreshRuleCache(String moduleName) {
        if (moduleName == null) {
            // 刷新所有模块
            moduleContextCache.clear();
            log.info("已刷新所有模块的规则缓存");
        } else {
            // 仅刷新指定模块
            moduleContextCache.remove(moduleName);
            log.info("已刷新模块[{}]的规则缓存", moduleName);
        }
    }
}