package com.hdkj.lt.bf.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.hdkj.lt.bf.entity.FhzgSeCurrentEvent;
import com.hdkj.lt.bf.entity.FhzgSeMonitorCurrent;
import com.hdkj.lt.bf.entity.FhzgSeMonitorVoltage;
import com.hdkj.lt.bf.entity.FhzgSeSnapshotDetail;
import com.hdkj.lt.bf.entity.FhzgSeVoltageEvent;
import com.hdkj.lt.bf.entity.dto.SeOverloadAlarmListReq;
import com.hdkj.lt.bf.entity.dto.SeVoltageAlarmListReq;
import com.hdkj.lt.bf.application.FeederGroupApplication;
import com.hdkj.lt.bf.entity.vo.*;
import com.hdkj.lt.bf.mapper.FhzgSeCurrentEventMapper;
import com.hdkj.lt.bf.mapper.FhzgSeMonitorCurrentMapper;
import com.hdkj.lt.bf.mapper.FhzgSeMonitorVoltageMapper;
import com.hdkj.lt.bf.mapper.FhzgSeSnapshotDetailMapper;
import com.hdkj.lt.bf.mapper.FhzgSeVoltageEventMapper;
import com.hdkj.lt.bf.service.SeAlarmService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 异常告警查询 Service 实现 V2
 *
 * @author lsl
 * @since 2026-07-23
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SeAlarmServiceImpl implements SeAlarmService {

    private final FhzgSeCurrentEventMapper currentEventMapper;
    private final FhzgSeVoltageEventMapper voltageEventMapper;
    private final FeederGroupApplication feederGroupApplication;
    private final FhzgSeMonitorCurrentMapper monitorCurrentMapper;
    private final FhzgSeMonitorVoltageMapper monitorVoltageMapper;
    private final FhzgSeSnapshotDetailMapper snapshotDetailMapper;

    private static final DateTimeFormatter DT_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private static final List<String> OVERLOAD_TYPES = Arrays.asList("current_heavy", "current_overload");
    private static final List<String> VOLTAGE_TYPES = Arrays.asList(
            "voltage_over", "voltage_under", "voltage_over_general", "voltage_over_severe",
            "voltage_under_general", "voltage_under_severe");

    // ============================================================
    // 1. 重过载列表
    // ============================================================

    @Override
    public PageResult<SeAlarmListVO> queryOverloadAlarmList(SeOverloadAlarmListReq req) {
        if (req.getPage() == null || req.getPage() < 1) req.setPage(1);
        if (req.getPageSize() == null || req.getPageSize() < 1) req.setPageSize(20);

        LocalDate start = parseDate(req.getStartDate());
        LocalDate end = parseDate(req.getEndDate());
        if (end == null || end.isBefore(start)) end = start;
        LocalDateTime rangeStart = start.atStartOfDay();
        LocalDateTime rangeEnd = end.plusDays(1).atStartOfDay();

        Page<FhzgSeCurrentEvent> mpPage = new Page<>(req.getPage(), req.getPageSize());

        LambdaQueryWrapper<FhzgSeCurrentEvent> wrapper = new LambdaQueryWrapper<FhzgSeCurrentEvent>()
                .ge(FhzgSeCurrentEvent::getFirstOverTime, rangeStart)
                .lt(FhzgSeCurrentEvent::getFirstOverTime, rangeEnd)
                .in(FhzgSeCurrentEvent::getAlarmType, OVERLOAD_TYPES);

        applyCurrentFilter(wrapper, req.getId(), req.getType());
        if (StringUtils.isNotBlank(req.getFeederName())) {
            wrapper.like(FhzgSeCurrentEvent::getFeederName, req.getFeederName());
        }

        // 负载率排序优先级高于时间排序
        applyOrderBy(wrapper, req.getOrderByLoadRate(), FhzgSeCurrentEvent::getMaxLoadRate);
        applyOrderBy(wrapper, req.getOrderByTime(), FhzgSeCurrentEvent::getFirstOverTime);

        Page<FhzgSeCurrentEvent> result = currentEventMapper.selectPage(mpPage, wrapper);
        List<SeAlarmListVO> voList = result.getRecords().stream().map(this::toOverloadVO).collect(Collectors.toList());
        return PageResult.of(result, voList);
    }

    private SeAlarmListVO toOverloadVO(FhzgSeCurrentEvent e) {
        String displayValue = e.getMaxLoadRate() != null
                ? e.getMaxLoadRate().setScale(0, java.math.RoundingMode.HALF_UP).toPlainString()
                : null;
        return SeAlarmListVO.builder()
                .alarmEventId(e.getId())
                .feederName(e.getFeederName())
                .feederId(e.getFeederId())
                .alarmDisplayValue(displayValue)
                .alarmTime(e.getFirstOverTime() != null ? e.getFirstOverTime().format(DT_FMT) : null)
                .alarmDuration(calcDuration(e.getFirstOverTime(), e.getRecoverTime(), e.getStatus()))
                .alarmType(e.getAlarmType())
                .alarmLevel("feeder")
                .build();
    }

    // ============================================================
    // 2. 电压越限列表
    // ============================================================

    @Override
    public PageResult<SeAlarmListVO> queryVoltageAlarmList(SeVoltageAlarmListReq req) {
        if (req.getPage() == null || req.getPage() < 1) req.setPage(1);
        if (req.getPageSize() == null || req.getPageSize() < 1) req.setPageSize(20);

        LocalDate start = parseDate(req.getStartDate());
        LocalDate end = parseDate(req.getEndDate());
        if (end == null || end.isBefore(start)) end = start;
        LocalDateTime rangeStart = start.atStartOfDay();
        LocalDateTime rangeEnd = end.plusDays(1).atStartOfDay();

        Page<FhzgSeVoltageEvent> mpPage = new Page<>(req.getPage(), req.getPageSize());
        LambdaQueryWrapper<FhzgSeVoltageEvent> wrapper = new LambdaQueryWrapper<FhzgSeVoltageEvent>()
                .ge(FhzgSeVoltageEvent::getFirstOverTime, rangeStart)
                .lt(FhzgSeVoltageEvent::getFirstOverTime, rangeEnd)
                .eq(FhzgSeVoltageEvent::getAlarmLevel, "feeder".equals(req.getAlarmLevel()) ? "feeder" : "mvtrans");

        applyVoltageFilter(wrapper, req.getId(), req.getType());
        if (StringUtils.isNotBlank(req.getFeederName())) {
            wrapper.like(FhzgSeVoltageEvent::getFeederName, req.getFeederName());
        }
        applyOrderBy(wrapper, req.getOrderByTime(), FhzgSeVoltageEvent::getFirstOverTime);

        Page<FhzgSeVoltageEvent> result = voltageEventMapper.selectPage(mpPage, wrapper);
        List<SeAlarmListVO> voList = result.getRecords().stream()
                .map(this::toVoltageVO)
                .collect(Collectors.toList());
        return PageResult.of(result, voList);
    }

    private SeAlarmListVO toVoltageVO(FhzgSeVoltageEvent e) {
        String displayValue = resolveVoltageDirection(e.getAlarmType());
        // 台区事件：查窗口内该配变下用户越限事件条数（嵌套在配变窗口内的，不去重）
        Integer consumerCount = null;
        if ("mvtrans".equals(e.getAlarmLevel()) && e.getMvtransId() != null) {
            LocalDateTime windowEnd = (e.getStatus() != null && e.getStatus() == 2 && e.getRecoverTime() != null)
                    ? e.getRecoverTime() : LocalDateTime.now();
            Long cnt = voltageEventMapper.selectCount(
                    new LambdaQueryWrapper<FhzgSeVoltageEvent>()
                            .eq(FhzgSeVoltageEvent::getAlarmLevel, "consumer")
                            .eq(FhzgSeVoltageEvent::getMvtransId, e.getMvtransId())
                            .ge(FhzgSeVoltageEvent::getFirstOverTime, e.getFirstOverTime())
                            .lt(FhzgSeVoltageEvent::getFirstOverTime, windowEnd));
            consumerCount = cnt != null ? cnt.intValue() : 0;
        }
        return SeAlarmListVO.builder()
                .alarmEventId(e.getId())
                .deviceName(e.getDeviceName())
                .feederName(e.getFeederName())
                .feederId(e.getFeederId())
                .alarmDisplayValue(displayValue)
                .alarmTime(e.getFirstOverTime() != null ? e.getFirstOverTime().format(DT_FMT) : null)
                .alarmDuration(calcDuration(e.getFirstOverTime(), e.getRecoverTime(), e.getStatus()))
                .alarmType(e.getAlarmType())
                .alarmLevel(e.getAlarmLevel())
                .affectedConsumerCount(consumerCount)
                .build();
    }

    /**
     * 电压越限方向（列表用）：只返回越上限/越下限，不带设备类型前缀
     */
    private String resolveVoltageDirection(String alarmType) {
        if (alarmType == null) return "电压越限";
        if (alarmType.contains("over")) return "越上限";
        if (alarmType.contains("under")) return "越下限";
        return "电压越限";
    }

    /**
     * 展示标签（详情用）：按 alarmType + alarmLevel 拼线路/台区电压越限
     */
    private String resolveVoltageDisplay(String alarmType, String alarmLevel) {
        boolean isOver = alarmType != null && alarmType.contains("over");
        boolean isUnder = alarmType != null && alarmType.contains("under");
        if ("mvtrans".equals(alarmLevel)) {
            if (isOver) return "台区电压越上限";
            if (isUnder) return "台区电压越下限";
            return "电压越限";
        }
        // 默认 feeder
        if (isOver) return "线路电压越上限";
        if (isUnder) return "线路电压越下限";
        return "电压越限";
    }

    // ============================================================
    // 3. 运行异常信息详情
    // ============================================================

    @Override
    public SeAlarmDetailVO queryAlarmDetail(Long alarmEventId) {
        if (alarmEventId == null) return null;

        // 重过载
        FhzgSeCurrentEvent currentEvent = currentEventMapper.selectById(alarmEventId);
        if (currentEvent != null) {
            return buildOverloadDetail(currentEvent);
        }

        // 电压事件
        FhzgSeVoltageEvent voltageEvent = voltageEventMapper.selectById(alarmEventId);
        if (voltageEvent != null && "feeder".equals(voltageEvent.getAlarmLevel())) {
            return buildFeederDetail(voltageEvent);
        }

        return null;
    }

    /**
     * 线路事件详情：展示电压范围 + 窗口内配变统计
     */
    private SeAlarmDetailVO buildFeederDetail(FhzgSeVoltageEvent feederEvent) {
        String feederId = feederEvent.getFeederId();
        // 持续窗口
        LocalDateTime endTime = feederEvent.getRecoverTime();
        if (endTime == null && feederEvent.getStatus() == 1) {
            FhzgSeMonitorVoltage state = monitorVoltageMapper.selectOne(
                    new LambdaQueryWrapper<FhzgSeMonitorVoltage>()
                            .eq(FhzgSeMonitorVoltage::getFeederId, feederId)
                            .eq(FhzgSeMonitorVoltage::getDeviceType, "feeder")
                            .last("LIMIT 1"));
            if (state != null) endTime = state.getLastSnapTime();
        }
        if (endTime == null) endTime = feederEvent.getTriggerTime();
        LocalDateTime rangeEnd = endTime != null ? endTime : LocalDateTime.now();

        // 实时查该馈线在越限时段内的电压范围
        BigDecimal maxVoltage = null, minVoltage = null;
        List<FhzgSeSnapshotDetail> snaps = snapshotDetailMapper.selectList(
                new LambdaQueryWrapper<FhzgSeSnapshotDetail>()
                        .eq(FhzgSeSnapshotDetail::getFeederId, feederId)
                        .eq(FhzgSeSnapshotDetail::getDeviceType, "feeder")
                        .isNotNull(FhzgSeSnapshotDetail::getVoltageValue)
                        .ge(FhzgSeSnapshotDetail::getSnapshotTime, feederEvent.getFirstOverTime())
                        .le(FhzgSeSnapshotDetail::getSnapshotTime, rangeEnd));
        for (FhzgSeSnapshotDetail s : snaps) {
            BigDecimal v = s.getVoltageValue();
            if (v == null) continue;
            if (maxVoltage == null || v.compareTo(maxVoltage) > 0) maxVoltage = v;
            if (minVoltage == null || v.compareTo(minVoltage) < 0) minVoltage = v;
        }

        // 窗口内配变统计（实时查，不依赖触发快照值）
        int[] mc = countMvtransInWindow(feederId, feederEvent.getFirstOverTime(), rangeEnd);

        return SeAlarmDetailVO.builder()
                .alarmEventId(feederEvent.getId())
                .alarmType(feederEvent.getAlarmType())
                .alarmCategory("voltage")
                .countyName(feederEvent.getCountyName())
                .feederName(feederEvent.getFeederName())
                .alarmStartTime(feederEvent.getFirstOverTime())
                .alarmEndTime(endTime)
                .abnormalIndicator(resolveVoltageDisplay(feederEvent.getAlarmType(), feederEvent.getAlarmLevel()))
                .maxVoltage(maxVoltage)
                .minVoltage(minVoltage)
                .overUpperMvtransCount(mc[0])
                .overLowerMvtransCount(mc[1])
                .reconStatus(feederEvent.getReconStatus())
                .build();
    }

    // ============================================================
    // 4. 重过载事件详情
    // ============================================================

    private SeAlarmDetailVO buildOverloadDetail(FhzgSeCurrentEvent event) {
        String abnormalIndicator = resolveOverloadDisplay(event.getAlarmType());
        return SeAlarmDetailVO.builder()
                .alarmEventId(event.getId())
                .alarmType(event.getAlarmType())
                .alarmCategory("overload")
                .countyName(event.getCountyName())
                .feederName(event.getFeederName())
                .alarmStartTime(event.getFirstOverTime())
                .alarmEndTime(event.getRecoverTime())
                .maxLoadRate(
                        event.getMaxLoadRate() != null
                                ? event.getMaxLoadRate().setScale(2, java.math.RoundingMode.HALF_UP)
                                : null)
                .abnormalIndicator(abnormalIndicator)
                .reconStatus(event.getReconStatus())
                .build();
    }

    /**
     * 解析重过载展示标签
     */
    private String resolveOverloadDisplay(String alarmType) {
        if ("current_overload".equals(alarmType)) return "线路过载";
        if ("current_heavy".equals(alarmType)) return "线路重载";
        return alarmType;
    }

    // ============================================================
    // 辅助：窗口内配变统计
    // ============================================================

    /**
     * 统计指定馈线在窗口内的配变越限数
     */
    private int[] countMvtransInWindow(String feederId, LocalDateTime start, LocalDateTime end) {
        List<FhzgSeVoltageEvent> mvEvents = voltageEventMapper.selectList(
                new LambdaQueryWrapper<FhzgSeVoltageEvent>()
                        .eq(FhzgSeVoltageEvent::getFeederId, feederId)
                        .eq(FhzgSeVoltageEvent::getAlarmLevel, "mvtrans")
                        .ge(FhzgSeVoltageEvent::getFirstOverTime, start)
                        .lt(FhzgSeVoltageEvent::getFirstOverTime, end));
        int up = 0, down = 0;
        for (FhzgSeVoltageEvent e : mvEvents) {
            if (e.getAlarmType() != null && e.getAlarmType().contains("over")) up++;
            else if (e.getAlarmType() != null && e.getAlarmType().contains("under")) down++;
        }
        return new int[]{up, down};
    }

    // ============================================================
    // 辅助
    // ============================================================

    private void applyCurrentFilter(LambdaQueryWrapper<FhzgSeCurrentEvent> wrapper, String id, Integer type) {
        if (type == 2) wrapper.eq(FhzgSeCurrentEvent::getCountyId, id);
        else if (type == 3) wrapper.eq(FhzgSeCurrentEvent::getSubsId, id);
        else if (type == 4) wrapper.eq(FhzgSeCurrentEvent::getFeederId, id);
    }

    private void applyVoltageFilter(LambdaQueryWrapper<FhzgSeVoltageEvent> wrapper, String id, Integer type) {
        if (type == 2) wrapper.eq(FhzgSeVoltageEvent::getCountyId, id);
        else if (type == 3) wrapper.eq(FhzgSeVoltageEvent::getSubsId, id);
        else if (type == 4) wrapper.eq(FhzgSeVoltageEvent::getFeederId, id);
    }

    // ============================================================
    // 线路下拉列表
    // ============================================================

    @Override
    public List<FeederSelectVO> queryFeederList(String id, Integer type) {
        List<FeederSelectVO> result = new java.util.ArrayList<>();
        if (id == null || type == null) return result;

        try {
            java.util.List<com.hdkj.fhzg.optimization.response.FeederGroupTreeNode> tree = feederGroupApplication.getFeederGroupTree();
            if (tree == null) return result;

            // 找到目标节点
            com.hdkj.fhzg.optimization.response.FeederGroupTreeNode target = findNode(tree, id, type);
            if (target == null) return result;

            // 收集其下所有 type=4 的馈线
            collectFeeders(target, result);
        } catch (Exception e) {
            log.warn("查询线路下拉列表失败 id={}, type={}", id, type, e);
        }
        return result;
    }

    private com.hdkj.fhzg.optimization.response.FeederGroupTreeNode findNode(
            java.util.List<com.hdkj.fhzg.optimization.response.FeederGroupTreeNode> nodes,
            String id, Integer type) {
        if (nodes == null) return null;
        for (com.hdkj.fhzg.optimization.response.FeederGroupTreeNode node : nodes) {
            if (type.equals(node.getType()) && id.equals(node.getId())) {
                return node;
            }
            if (node.getChildren() != null && !node.getChildren().isEmpty()) {
                com.hdkj.fhzg.optimization.response.FeederGroupTreeNode found = findNode(node.getChildren(), id, type);
                if (found != null) return found;
            }
        }
        return null;
    }

    // ============================================================
    // 辅助：日期解析 + 排序
    // ============================================================

    private LocalDate parseDate(String date) {
        if (StringUtils.isBlank(date)) return LocalDate.now();
        try {
            return LocalDate.parse(date);
        } catch (Exception e) {
            log.warn("日期格式非法: {}, 默认当天", date);
            return LocalDate.now();
        }
    }

    /**
     * 追加排序条件，orderDir 为空时不追加任何排序
     * 多个字段排序时按调用顺序决定优先级，先调用的优先
     */
    private <T> void applyOrderBy(LambdaQueryWrapper<T> wrapper, String orderDir,
                                   SFunction<T, ?> column) {
        if (StringUtils.isBlank(orderDir)) return;
        if ("asc".equalsIgnoreCase(orderDir)) {
            wrapper.orderByAsc(column);
        } else {
            wrapper.orderByDesc(column);
        }
    }

    /**
     * 计算告警持续时长
     * @return 如 "1.5h"，已结束用 recoverTime，活跃用当前时间
     */
    private String calcDuration(LocalDateTime start, LocalDateTime end, Integer status) {
        if (start == null) return null;
        LocalDateTime actualEnd = (status != null && status == 2 && end != null) ? end : LocalDateTime.now();
        long minutes = Duration.between(start, actualEnd).toMinutes();
        if (minutes < 0) minutes = 0;
        return String.format("%.1fh", minutes / 60.0);
    }

    private void collectFeeders(com.hdkj.fhzg.optimization.response.FeederGroupTreeNode node,
                                 java.util.List<FeederSelectVO> result) {
        if (node.getType() == 4) {
            result.add(new FeederSelectVO(node.getId(), node.getName()));
        }
        if (node.getChildren() != null) {
            for (com.hdkj.fhzg.optimization.response.FeederGroupTreeNode child : node.getChildren()) {
                collectFeeders(child, result);
            }
        }
    }
}
