package com.xzzn.quartz.task;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xzzn.common.annotation.SyncAfterInsert;
import com.xzzn.common.constant.RedisKeyConstants;
import com.xzzn.common.core.redis.RedisCache;
import com.xzzn.common.enums.AlarmLevelStatus;
import com.xzzn.common.enums.AlarmStatus;
import com.xzzn.common.enums.ProtPlanStatus;
import com.xzzn.common.utils.StringUtils;
import com.xzzn.ems.domain.EmsAlarmRecords;
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
import com.xzzn.ems.domain.vo.ProtectionConstraintVo;
import com.xzzn.ems.domain.vo.ProtectionPlanVo;
import com.xzzn.ems.domain.vo.ProtectionSettingVo;
import com.xzzn.ems.domain.vo.ProtectionSettingsGroupVo;
import com.xzzn.ems.mapper.EmsAlarmRecordsMapper;
import com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper;
import com.xzzn.ems.service.IEmsFaultProtectionPlanService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 告警保护方案轮询
 */
@Component("protectionPlanTask")
public class ProtectionPlanTask {
    private static final Logger logger = LoggerFactory.getLogger(ProtectionPlanTask.class);
    private static final BigDecimal DEFAULT_L1_POWER_RATIO = new BigDecimal("0.5");
    private static final int CONSTRAINT_TTL_SECONDS = 120;

    @Resource(name = "scheduledExecutorService")
    private ScheduledExecutorService scheduledExecutorService;
    @Autowired
    private IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService;
    @Autowired
    private EmsAlarmRecordsMapper emsAlarmRecordsMapper;
    @Autowired
    private EmsFaultProtectionPlanMapper emsFaultProtectionPlanMapper;
    @Autowired
    private RedisCache redisCache;

    private static final ObjectMapper objectMapper = new ObjectMapper();

    public ProtectionPlanTask(IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService) {
        this.iEmsFaultProtectionPlanService = iEmsFaultProtectionPlanService;
    }

    public void pollPlanList() {
        Long planId = 0L;
        try {
            List<EmsFaultProtectionPlan> planList = iEmsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(null);
            for (EmsFaultProtectionPlan plan : planList) {
                planId = plan.getId();
                String siteId = plan.getSiteId();
                if (StringUtils.isEmpty(siteId)) {
                    continue;
                }
                ProtectionSettingsGroupVo settingGroup = parseProtectionSettings(plan.getProtectionSettings());
                if (CollectionUtils.isEmpty(settingGroup.getFaultSettings())
                        && CollectionUtils.isEmpty(settingGroup.getReleaseSettings())) {
                    continue;
                }
                dealWithProtectionPlan(plan, settingGroup);
            }
            refreshProtectionConstraintCache(planList);
        } catch (Exception e) {
            logger.error("轮询失败,方案id为：{}", planId, e);
        }
    }

    @SyncAfterInsert
    private void dealWithProtectionPlan(EmsFaultProtectionPlan plan, ProtectionSettingsGroupVo settingGroup) {
        logger.info("<轮询保护方案> 站点:{}，方案ID:{}", plan.getSiteId(), plan.getId());
        String siteId = plan.getSiteId();
        Integer isAlertAlarm = plan.getIsAlert();
        List<ProtectionSettingVo> faultSettings = settingGroup.getFaultSettings();
        List<ProtectionSettingVo> releaseSettings = settingGroup.getReleaseSettings();
        Long status = plan.getStatus();
        if (status == null) {
            status = ProtPlanStatus.STOP.getCode();
        }

        if (Objects.equals(status, ProtPlanStatus.STOP.getCode())) {
            if (checkIsNeedIssuedPlan(faultSettings, siteId)) {
                int faultDelay = safeDelaySeconds(plan.getFaultDelaySeconds(), 0);
                scheduledExecutorService.schedule(() -> {
                    if (!checkIsNeedIssuedPlan(faultSettings, siteId)) {
                        return;
                    }
                    if (Integer.valueOf(1).equals(isAlertAlarm)) {
                        EmsAlarmRecords alarmRecords = addAlarmRecord(siteId, plan.getFaultName(), getAlarmLevel(plan.getFaultLevel()));
                        emsAlarmRecordsMapper.insertEmsAlarmRecords(alarmRecords);
                    }
                    plan.setStatus(ProtPlanStatus.RUNNING.getCode());
                    plan.setUpdateBy("system");
                    emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
                    refreshSiteProtectionConstraint(siteId);
                }, faultDelay, TimeUnit.SECONDS);
            }
            return;
        }

        if (checkIsNeedCancelPlan(releaseSettings, siteId)) {
            int releaseDelay = safeDelaySeconds(plan.getReleaseDelaySeconds(), 0);
            scheduledExecutorService.schedule(() -> {
                if (Integer.valueOf(1).equals(isAlertAlarm)) {
                    EmsAlarmRecords emsAlarmRecords = emsAlarmRecordsMapper.getFailedRecord(
                            siteId,
                            plan.getFaultName(),
                            getAlarmLevel(plan.getFaultLevel())
                    );
                    if (emsAlarmRecords != null) {
                        emsAlarmRecords.setStatus(AlarmStatus.DONE.getCode());
                        emsAlarmRecordsMapper.updateEmsAlarmRecords(emsAlarmRecords);
                    }
                }
                plan.setStatus(ProtPlanStatus.STOP.getCode());
                plan.setUpdateBy("system");
                emsFaultProtectionPlanMapper.updateEmsFaultProtectionPlan(plan);
                refreshSiteProtectionConstraint(siteId);
            }, releaseDelay, TimeUnit.SECONDS);
        }
    }

    private int safeDelaySeconds(Long delay, int defaultSeconds) {
        if (delay == null || delay < 0) {
            return defaultSeconds;
        }
        return delay.intValue();
    }

    private ProtectionSettingsGroupVo parseProtectionSettings(String settingsJson) {
        if (StringUtils.isEmpty(settingsJson)) {
            return ProtectionSettingsGroupVo.empty();
        }
        try {
            if (settingsJson.trim().startsWith("[")) {
                List<ProtectionSettingVo> legacy = objectMapper.readValue(
                        settingsJson,
                        new TypeReference<List<ProtectionSettingVo>>() {}
                );
                ProtectionSettingsGroupVo groupVo = ProtectionSettingsGroupVo.empty();
                groupVo.setFaultSettings(legacy);
                groupVo.setReleaseSettings(legacy);
                return groupVo;
            }
            ProtectionSettingsGroupVo groupVo = objectMapper.readValue(settingsJson, ProtectionSettingsGroupVo.class);
            if (groupVo == null) {
                return ProtectionSettingsGroupVo.empty();
            }
            if (groupVo.getFaultSettings() == null) {
                groupVo.setFaultSettings(new ArrayList<>());
            }
            if (groupVo.getReleaseSettings() == null) {
                groupVo.setReleaseSettings(new ArrayList<>());
            }
            return groupVo;
        } catch (Exception e) {
            logger.error("解析保护前提失败，json:{}", settingsJson, e);
            return ProtectionSettingsGroupVo.empty();
        }
    }

    private List<ProtectionPlanVo> parseProtectionPlan(String planJson) {
        if (StringUtils.isEmpty(planJson)) {
            return new ArrayList<>();
        }
        try {
            if (planJson.trim().startsWith("[")) {
                List<ProtectionPlanVo> plans = objectMapper.readValue(
                        planJson,
                        new TypeReference<List<ProtectionPlanVo>>() {}
                );
                return plans == null ? new ArrayList<>() : plans;
            }
            ProtectionPlanVo plan = objectMapper.readValue(planJson, ProtectionPlanVo.class);
            List<ProtectionPlanVo> plans = new ArrayList<>();
            if (plan != null) {
                plans.add(plan);
            }
            return plans;
        } catch (Exception e) {
            logger.error("解析执行保护失败，json:{}", planJson, e);
            return new ArrayList<>();
        }
    }

    private void refreshProtectionConstraintCache(List<EmsFaultProtectionPlan> allPlans) {
        Map<String, List<EmsFaultProtectionPlan>> planBySite = new HashMap<>();
        for (EmsFaultProtectionPlan plan : allPlans) {
            if (StringUtils.isEmpty(plan.getSiteId())) {
                continue;
            }
            planBySite.computeIfAbsent(plan.getSiteId(), k -> new ArrayList<>()).add(plan);
        }
        for (Map.Entry<String, List<EmsFaultProtectionPlan>> entry : planBySite.entrySet()) {
            writeSiteProtectionConstraint(entry.getKey(), entry.getValue());
        }
    }

    private void refreshSiteProtectionConstraint(String siteId) {
        if (StringUtils.isEmpty(siteId)) {
            return;
        }
        EmsFaultProtectionPlan query = new EmsFaultProtectionPlan();
        query.setSiteId(siteId);
        List<EmsFaultProtectionPlan> sitePlans = iEmsFaultProtectionPlanService.selectEmsFaultProtectionPlanList(query);
        writeSiteProtectionConstraint(siteId, sitePlans);
    }

    private void writeSiteProtectionConstraint(String siteId, List<EmsFaultProtectionPlan> sitePlans) {
        List<EmsFaultProtectionPlan> runningPlans = new ArrayList<>();
        for (EmsFaultProtectionPlan plan : sitePlans) {
            if (Objects.equals(plan.getStatus(), ProtPlanStatus.RUNNING.getCode())) {
                runningPlans.add(plan);
            }
        }

        String key = RedisKeyConstants.PROTECTION_CONSTRAINT + siteId;
        if (runningPlans.isEmpty()) {
            redisCache.deleteObject(key);
            return;
        }

        ProtectionConstraintVo merged = ProtectionConstraintVo.empty();
        for (EmsFaultProtectionPlan runningPlan : runningPlans) {
            ProtectionConstraintVo single = buildConstraintFromPlan(runningPlan);
            mergeConstraint(merged, single);
        }
        merged.setUpdateAt(System.currentTimeMillis());
        redisCache.setCacheObject(key, merged, CONSTRAINT_TTL_SECONDS, TimeUnit.SECONDS);
    }

    private ProtectionConstraintVo buildConstraintFromPlan(EmsFaultProtectionPlan plan) {
        ProtectionConstraintVo vo = ProtectionConstraintVo.empty();
        int level = plan.getFaultLevel() == null ? 0 : plan.getFaultLevel();
        vo.setLevel(level);
        vo.setSourcePlanIds(new ArrayList<>());
        vo.getSourcePlanIds().add(plan.getId());

        if (level == 1) {
            vo.setPowerLimitRatio(DEFAULT_L1_POWER_RATIO);
        } else if (level >= 3) {
            vo.setForceStop(true);
            vo.setForceStandby(true);
            vo.setAllowCharge(false);
            vo.setAllowDischarge(false);
            vo.setPowerLimitRatio(BigDecimal.ZERO);
        }

        String description = StringUtils.isEmpty(plan.getDescription()) ? "" : plan.getDescription();
        BigDecimal ratioByDesc = parseDerateRatio(description);
        if (ratioByDesc != null) {
            vo.setPowerLimitRatio(minRatio(vo.getPowerLimitRatio(), ratioByDesc));
        }

        if (description.contains("禁止充放电")) {
            vo.setAllowCharge(false);
            vo.setAllowDischarge(false);
        } else {
            if (description.contains("禁止充电")) {
                vo.setAllowCharge(false);
            }
            if (description.contains("禁止放电")) {
                vo.setAllowDischarge(false);
            }
            if (description.contains("允许充电")) {
                vo.setAllowCharge(true);
            }
            if (description.contains("允许放电")) {
                vo.setAllowDischarge(true);
            }
        }

        if (description.contains("待机")) {
            vo.setForceStandby(true);
        }
        if (description.contains("停机") || description.contains("切断") || level >= 3) {
            vo.setForceStop(true);
            vo.setForceStandby(true);
            vo.setAllowCharge(false);
            vo.setAllowDischarge(false);
            vo.setPowerLimitRatio(BigDecimal.ZERO);
        }

        // 执行保护配置优先于描述文本配置
        List<ProtectionPlanVo> protectionPlans = parseProtectionPlan(plan.getProtectionPlan());
        applyCapabilityByProtectionPlan(vo, protectionPlans);

        return vo;
    }

    private void applyCapabilityByProtectionPlan(ProtectionConstraintVo vo, List<ProtectionPlanVo> protectionPlans) {
        if (CollectionUtils.isEmpty(protectionPlans)) {
            return;
        }
        for (ProtectionPlanVo item : protectionPlans) {
            if (item == null) {
                continue;
            }
            String marker = ((item.getPointName() == null ? "" : item.getPointName()) + " "
                    + (item.getPoint() == null ? "" : item.getPoint())).toLowerCase();
            if (StringUtils.isEmpty(marker)) {
                continue;
            }

            if (containsAny(marker, "降功率", "derate", "power_limit", "powerlimit")) {
                BigDecimal ratio = parseDerateRatioByPlan(item);
                if (ratio != null) {
                    vo.setPowerLimitRatio(minRatio(vo.getPowerLimitRatio(), ratio));
                }
            }

            if (!isCapabilityEnabled(item.getValue())) {
                continue;
            }

            if (containsAny(marker, "禁止充放电", "forbid_charge_discharge", "disable_charge_discharge")) {
                vo.setAllowCharge(false);
                vo.setAllowDischarge(false);
                continue;
            }
            if (containsAny(marker, "禁止充电", "forbid_charge", "disable_charge")) {
                vo.setAllowCharge(false);
            }
            if (containsAny(marker, "禁止放电", "forbid_discharge", "disable_discharge")) {
                vo.setAllowDischarge(false);
            }
            if (containsAny(marker, "允许充电", "allow_charge")) {
                vo.setAllowCharge(true);
            }
            if (containsAny(marker, "允许放电", "allow_discharge")) {
                vo.setAllowDischarge(true);
            }
            if (containsAny(marker, "待机", "standby")) {
                vo.setForceStandby(true);
            }
            if (containsAny(marker, "关机", "停机", "切断", "shutdown", "stop")) {
                vo.setForceStop(true);
                vo.setForceStandby(true);
                vo.setAllowCharge(false);
                vo.setAllowDischarge(false);
                vo.setPowerLimitRatio(BigDecimal.ZERO);
            }
        }
    }

    private BigDecimal parseDerateRatioByPlan(ProtectionPlanVo planVo) {
        BigDecimal value = planVo.getValue();
        if (value == null || value.compareTo(BigDecimal.ZERO) < 0) {
            return null;
        }
        if (value.compareTo(BigDecimal.ONE) <= 0) {
            return value;
        }
        if (value.compareTo(new BigDecimal("100")) <= 0) {
            return value.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
        }
        return null;
    }

    private boolean isCapabilityEnabled(BigDecimal value) {
        return value == null || value.compareTo(BigDecimal.ZERO) != 0;
    }

    private boolean containsAny(String text, String... markers) {
        if (StringUtils.isEmpty(text) || markers == null) {
            return false;
        }
        for (String marker : markers) {
            if (!StringUtils.isEmpty(marker) && text.contains(marker)) {
                return true;
            }
        }
        return false;
    }

    private BigDecimal parseDerateRatio(String text) {
        if (StringUtils.isEmpty(text)) {
            return null;
        }
        Matcher m = Pattern.compile("降功率\\s*(\\d+(?:\\.\\d+)?)%")
                .matcher(text);
        if (!m.find()) {
            return null;
        }
        BigDecimal percent = new BigDecimal(m.group(1));
        if (percent.compareTo(BigDecimal.ZERO) < 0) {
            return null;
        }
        return percent.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
    }

    private void mergeConstraint(ProtectionConstraintVo merged, ProtectionConstraintVo incoming) {
        if (incoming == null) {
            return;
        }
        merged.setLevel(Math.max(nullSafeInt(merged.getLevel()), nullSafeInt(incoming.getLevel())));
        merged.setAllowCharge(boolAnd(merged.getAllowCharge(), incoming.getAllowCharge()));
        merged.setAllowDischarge(boolAnd(merged.getAllowDischarge(), incoming.getAllowDischarge()));
        merged.setForceStandby(boolOr(merged.getForceStandby(), incoming.getForceStandby()));
        merged.setForceStop(boolOr(merged.getForceStop(), incoming.getForceStop()));
        merged.setPowerLimitRatio(minRatio(merged.getPowerLimitRatio(), incoming.getPowerLimitRatio()));

        if (incoming.getSourcePlanIds() != null && !incoming.getSourcePlanIds().isEmpty()) {
            if (merged.getSourcePlanIds() == null) {
                merged.setSourcePlanIds(new ArrayList<>());
            }
            merged.getSourcePlanIds().addAll(incoming.getSourcePlanIds());
        }
    }

    private BigDecimal minRatio(BigDecimal a, BigDecimal b) {
        BigDecimal left = a == null ? BigDecimal.ONE : a;
        BigDecimal right = b == null ? BigDecimal.ONE : b;
        return left.min(right);
    }

    private int nullSafeInt(Integer value) {
        return value == null ? 0 : value;
    }

    private Boolean boolAnd(Boolean a, Boolean b) {
        boolean left = a == null || a;
        boolean right = b == null || b;
        return left && right;
    }

    private Boolean boolOr(Boolean a, Boolean b) {
        boolean left = a != null && a;
        boolean right = b != null && b;
        return left || right;
    }

    private boolean checkIsNeedCancelPlan(List<ProtectionSettingVo> protSettings, String siteId) {
        StringBuilder conditionSb = new StringBuilder();
        for (int i = 0; i < protSettings.size(); i++) {
            ProtectionSettingVo vo = protSettings.get(i);
            String deviceId = vo.getDeviceId();
            String point = vo.getPoint();
            BigDecimal releaseValue = vo.getReleaseValue();
            if (StringUtils.isEmpty(deviceId)
                    || StringUtils.isEmpty(point)
                    || releaseValue == null
                    || StringUtils.isEmpty(vo.getReleaseOperator())) {
                return false;
            }
            BigDecimal lastPointValue = getPointLastValue(deviceId, point, siteId);
            if (lastPointValue == null) {
                return false;
            }

            conditionSb.append(lastPointValue).append(vo.getReleaseOperator()).append(releaseValue);
            if (i < protSettings.size() - 1) {
                conditionSb.append(" ").append(vo.getRelationNext()).append(" ");
            }
        }
        return executeWithParser(conditionSb.toString());
    }

    private boolean checkIsNeedIssuedPlan(List<ProtectionSettingVo> protSettings, String siteId) {
        StringBuilder conditionSb = new StringBuilder();
        for (int i = 0; i < protSettings.size(); i++) {
            ProtectionSettingVo vo = protSettings.get(i);
            String deviceId = vo.getDeviceId();
            String point = vo.getPoint();
            BigDecimal faultValue = vo.getFaultValue();
            if (StringUtils.isEmpty(deviceId)
                    || StringUtils.isEmpty(point)
                    || faultValue == null
                    || StringUtils.isEmpty(vo.getFaultOperator())) {
                return false;
            }
            BigDecimal lastPointValue = getPointLastValue(deviceId, point, siteId);
            if (lastPointValue == null) {
                return false;
            }

            conditionSb.append(lastPointValue).append(vo.getFaultOperator()).append(faultValue);
            if (i < protSettings.size() - 1) {
                conditionSb.append(" ").append(vo.getRelationNext()).append(" ");
            }
        }
        return executeWithParser(conditionSb.toString());
    }

    private BigDecimal getPointLastValue(String deviceId, String point, String siteId) {
        JSONObject mqttJson = redisCache.getCacheObject(RedisKeyConstants.SYNC_DATA + siteId + "_" + deviceId);
        if (mqttJson == null) {
            return null;
        }
        String jsonData = mqttJson.get("Data").toString();
        if (StringUtils.isEmpty(jsonData)) {
            return null;
        }
        Map<String, Object> obj = JSON.parseObject(jsonData, new com.alibaba.fastjson2.TypeReference<Map<String, Object>>() {});
        return StringUtils.getBigDecimal(obj.get(point));
    }

    private EmsAlarmRecords addAlarmRecord(String siteId, String content, String level) {
        EmsAlarmRecords emsAlarmRecords = new EmsAlarmRecords();
        emsAlarmRecords.setSiteId(siteId);
        emsAlarmRecords.setAlarmContent(content);
        emsAlarmRecords.setAlarmLevel(level);
        emsAlarmRecords.setAlarmStartTime(new Date());
        emsAlarmRecords.setStatus(AlarmStatus.WAITING.getCode());
        emsAlarmRecords.setDeviceType("TCP");
        emsAlarmRecords.setCreateBy("system");
        emsAlarmRecords.setCreateTime(new Date());
        return emsAlarmRecords;
    }

    private String getAlarmLevel(Integer faultLevel) {
        if (ObjectUtils.isEmpty(faultLevel) || faultLevel < 1 || faultLevel > 3) {
            logger.warn("非法故障等级：{}，默认返回紧急告警", faultLevel);
            return AlarmLevelStatus.EMERGENCY.getCode();
        }
        switch (faultLevel) {
            case 1:
                return AlarmLevelStatus.GENERAL.getCode();
            case 2:
                return AlarmLevelStatus.SERIOUS.getCode();
            case 3:
                return AlarmLevelStatus.EMERGENCY.getCode();
            default:
                return AlarmLevelStatus.EMERGENCY.getCode();
        }
    }

    /**
     * 自定义表达式解析器（仅支持简单运算符和逻辑关系）
     */
    public boolean executeWithParser(String conditionStr) {
        if (conditionStr == null || conditionStr.isEmpty()) {
            return false;
        }

        List<String> logicRelations = new ArrayList<>();
        Pattern logicPattern = Pattern.compile("(&&|\\|\\|)");
        Matcher logicMatcher = logicPattern.matcher(conditionStr);
        while (logicMatcher.find()) {
            logicRelations.add(logicMatcher.group());
        }

        String[] atomicConditions = logicPattern.split(conditionStr);
        List<Boolean> atomicResults = new ArrayList<>();
        Pattern conditionPattern = Pattern.compile("(\\d+\\.?\\d*)\\s*([><]=?|==)\\s*(\\d+\\.?\\d*)");
        for (String atomic : atomicConditions) {
            Matcher matcher = conditionPattern.matcher(atomic.trim());
            if (!matcher.matches()) {
                logger.error("无效的原子条件：{}", atomic);
                return false;
            }
            double left = Double.parseDouble(matcher.group(1));
            String operator = matcher.group(2);
            double right = Double.parseDouble(matcher.group(3));

            boolean result;
            switch (operator) {
                case ">":
                    result = left > right;
                    break;
                case ">=":
                    result = left >= right;
                    break;
                case "<":
                    result = left < right;
                    break;
                case "<=":
                    result = left <= right;
                    break;
                case "==":
                    result = left == right;
                    break;
                default:
                    result = false;
                    break;
            }
            atomicResults.add(result);
        }

        boolean finalResult = atomicResults.get(0);
        for (int i = 0; i < logicRelations.size(); i++) {
            String relation = logicRelations.get(i);
            boolean nextResult = atomicResults.get(i + 1);
            if ("&&".equals(relation)) {
                finalResult = finalResult && nextResult;
            } else if ("||".equals(relation)) {
                finalResult = finalResult || nextResult;
            }
        }
        return finalResult;
    }
}
