package com.njcn.csharmonic.service.impl.event;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.ImmutableMap;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.csdevice.api.CsCommTerminalFeignClient;
import com.njcn.csdevice.api.CsLineFeignClient;
import com.njcn.csdevice.pojo.dto.DevDetailDTO;
import com.njcn.csdevice.pojo.po.CsLinePO;
import com.njcn.csharmonic.enums.CsEventEnum;
import com.njcn.csharmonic.pojo.param.EventStatisticParam;
import com.njcn.csharmonic.pojo.po.CsEventPO;
import com.njcn.csharmonic.pojo.vo.event.EventCoordsVO;
import com.njcn.csharmonic.pojo.vo.event.EventStatisticVO;
import com.njcn.csharmonic.pojo.vo.event.F47Curve;
import com.njcn.csharmonic.service.CsEventPOService;
import com.njcn.csharmonic.service.event.EventOverviewService;
import com.njcn.event.common.mapper.WlRmpEventDetailMapper;
import com.njcn.event.pojo.po.RmpEventDetailPO;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.njcn.csharmonic.enums.CsTransientEnum.*;

/**
 * @Author: cdf
 * @CreateTime: 2025-11-20
 * @Description:
 */
@Service
@RequiredArgsConstructor
public class EventOverviewServiceImpl implements EventOverviewService {
    private static final List<String> EVENT_TAGS = Arrays.asList(
            EVT_SYS_DIPSTR.getCode(),
            EVT_SYS_INTRSTR.getCode(),
            EVT_SYS_SWLSTR.getCode()
    );

    private final CsEventPOService csEventPOService;
    private final CsLineFeignClient csLineFeignClient;
    private final CsCommTerminalFeignClient csCommTerminalFeignClient;
    private final WlRmpEventDetailMapper wlRmpEventDetailMapper;
    private final CsCommTerminalFeignClient csCommTerminal;

    @Override
    public EventStatisticVO netEventEcharts(EventStatisticParam baseParam) {
        EventStatisticVO result = new EventStatisticVO();
        DateTime start = DateUtil.beginOfDay(DateUtil.parse(baseParam.getSearchBeginTime()));
        DateTime end = DateUtil.endOfDay(DateUtil.parse(baseParam.getSearchEndTime()));

        List<String> ids = csCommTerminalFeignClient.getLineIdsByUser(RequestUtil.getUserIndex()).getData();
        if(CollUtil.isEmpty(ids)){
            return result;
        }
        List<CsEventPO> csEventPOList = csEventPOService.lambdaQuery()
                .between(CsEventPO::getStartTime, start, end)
                .eq(CsEventPO::getType, CsEventEnum.EVENT_TYPE.getCode())
                .in(CsEventPO::getTag, EVENT_TAGS)
                .in(CsEventPO::getLineId,ids)
                .list();


        if (CollUtil.isEmpty(csEventPOList)) {
            return result;
        }
        Map<String, Long> eventCountByTag = csEventPOList.stream().collect(Collectors.groupingBy(CsEventPO::getTag, Collectors.counting()));
        result.setEventDown(eventCountByTag.getOrDefault(EVT_SYS_DIPSTR.getCode(), 0L).intValue());
        result.setEventOff(eventCountByTag.getOrDefault(EVT_SYS_INTRSTR.getCode(), 0L).intValue());
        result.setEventUp(eventCountByTag.getOrDefault(EVT_SYS_SWLSTR.getCode(), 0L).intValue());
        result.setAllNum(csEventPOList.size());
        return result;
    }

    @Override
    public List<EventStatisticVO> netEventTable(EventStatisticParam baseParam) {
        DateTime start = DateUtil.beginOfDay(DateUtil.parse(baseParam.getSearchBeginTime()));
        DateTime end = DateUtil.endOfDay(DateUtil.parse(baseParam.getSearchEndTime()));

        List<String> lineIds = StrUtil.isBlank(baseParam.getSearchValue())
                ? csCommTerminalFeignClient.getLineIdsByUser(RequestUtil.getUserIndex()).getData()
                : Collections.singletonList(baseParam.getSearchValue());

        if(CollUtil.isEmpty(lineIds)){
            return Collections.emptyList();
        }
        List<CsLinePO> csLinePOList = csLineFeignClient.queryLineById(lineIds).getData();
        if (CollUtil.isEmpty(csLinePOList)) {
            return Collections.emptyList();
        }

        List<String> validLineIds = csLinePOList.stream()
                .map(CsLinePO::getLineId)
                .distinct()
                .collect(Collectors.toList());

        List<CsEventPO> csEventPOList = csEventPOService.lambdaQuery()
                .between(CsEventPO::getStartTime, start, end)
                .eq(CsEventPO::getType, CsEventEnum.EVENT_TYPE.getCode())
                .in(CsEventPO::getTag, EVENT_TAGS)
                .in(CsEventPO::getLineId, validLineIds)
                .list();

        if (CollUtil.isEmpty(csEventPOList)) {
            return csLinePOList.stream()
                    .map(line -> {
                        EventStatisticVO vo = new EventStatisticVO();
                        vo.setName(line.getName());
                        vo.setId(line.getLineId());
                        return vo;
                    })
                    .collect(Collectors.toList());
        }

        // 按线路ID和事件类型分组统计
        Map<String, Map<String, Long>> eventCountByLineAndTag = csEventPOList.stream()
                .collect(Collectors.groupingBy(
                        CsEventPO::getLineId,
                        Collectors.groupingBy(
                                CsEventPO::getTag,
                                Collectors.counting()
                        )
                ));

        List<DevDetailDTO> devDetailDTOList = csCommTerminal.getLedgerByLineId(lineIds).getData();
        Map<String, DevDetailDTO> devDetailDTOMap = devDetailDTOList.stream().collect(Collectors.toMap(DevDetailDTO::getLineId, item -> item));

        return csLinePOList.stream()
                .map(line -> {
                    EventStatisticVO vo = new EventStatisticVO();
                    vo.setEngineeringName(devDetailDTOMap.get(line.getLineId()).getEngineeringName());
                    vo.setProjectName(devDetailDTOMap.get(line.getLineId()).getProjectName());
                    vo.setDevName(devDetailDTOMap.get(line.getLineId()).getEquipmentName());
                    vo.setName(line.getName());
                    vo.setId(line.getLineId());
                    Map<String, Long> lineEvents = eventCountByLineAndTag.getOrDefault(line.getLineId(), Collections.emptyMap());
                    vo.setEventDown(lineEvents.getOrDefault(EVT_SYS_DIPSTR.getCode(), 0L).intValue());
                    vo.setEventOff(lineEvents.getOrDefault(EVT_SYS_INTRSTR.getCode(), 0L).intValue());
                    vo.setEventUp(lineEvents.getOrDefault(EVT_SYS_SWLSTR.getCode(), 0L).intValue());

                    return vo;
                }).sorted(Comparator.comparing(EventStatisticVO::getEventDown, Comparator.reverseOrder())
                        .thenComparing(EventStatisticVO::getEventOff, Comparator.reverseOrder()).thenComparing(EventStatisticVO::getEventUp, Comparator.reverseOrder()))
                .collect(Collectors.toList());
    }

    @Override
    public List<F47Curve> getF47Curve(EventStatisticParam baseParam) {
        DateTime start = DateUtil.beginOfDay(DateUtil.parse(baseParam.getSearchBeginTime()));
        DateTime end = DateUtil.endOfDay(DateUtil.parse(baseParam.getSearchEndTime()));

        List<String> ids = csCommTerminalFeignClient.getLineIdsByUser(RequestUtil.getUserIndex()).getData();
        if(CollUtil.isEmpty(ids)){
            return Collections.emptyList();
        }

        List<CsEventPO> csEventPOList = csEventPOService.lambdaQuery().select(CsEventPO::getId,CsEventPO::getLineId,CsEventPO::getStartTime,CsEventPO::getTag,CsEventPO::getPersistTime,CsEventPO::getAmplitude)
                .between(CsEventPO::getStartTime, start, end)
                .eq(CsEventPO::getType, CsEventEnum.EVENT_TYPE.getCode())
                .in(CsEventPO::getTag, EVENT_TAGS)
                .in(CsEventPO::getLineId,ids)
                .list();

        if(CollUtil.isEmpty(csEventPOList)){
            return Collections.emptyList();
        }
        List<String> monitorIds = csEventPOList.stream().map(CsEventPO::getLineId).distinct().collect(Collectors.toList());
        List<CsLinePO> csLinePOList = csLineFeignClient.queryLineById(monitorIds).getData();
        Map<String,String> nameMap = csLinePOList.stream().collect(Collectors.toMap(CsLinePO::getLineId, CsLinePO::getName));

        List<DevDetailDTO> devDetailDTOList = csCommTerminalFeignClient.getLedgerByLineId(monitorIds).getData();
        Map<String, DevDetailDTO> devDetailDTOMap = devDetailDTOList.stream().collect(Collectors.toMap(DevDetailDTO::getLineId, item -> item));
        List<F47Curve> f47CurveList = csEventPOList.stream().map(item->{
            F47Curve f47Curve = new F47Curve();
            f47Curve.setEventId(item.getId());
            f47Curve.setTime(item.getStartTime());
            f47Curve.setTag(item.getTag());
            f47Curve.setPersistTime(BigDecimal.valueOf(item.getPersistTime()).setScale(3, RoundingMode.HALF_UP));
            f47Curve.setEventValue(BigDecimal.valueOf(item.getAmplitude()));
            f47Curve.setEngineeringName(devDetailDTOMap.get(item.getLineId()).getEngineeringName());
            f47Curve.setProjectName(devDetailDTOMap.get(item.getLineId()).getProjectName());
            f47Curve.setEquipmentName(devDetailDTOMap.get(item.getLineId()).getEquipmentName());
            f47Curve.setLineName(nameMap.getOrDefault(item.getLineId(),"异常测点"));
            return f47Curve;
        }).collect(Collectors.toList());
        return f47CurveList;
    }

    @Override
    public List<EventStatisticVO> getEventDate(EventStatisticParam baseParam) {
        if(StrUtil.isBlank(baseParam.getSearchBeginTime())){
            throw new BusinessException(CommonResponseEnum.FAIL,"月份不可为空");
        }
        DateTime dateTime = DateUtil.parse(baseParam.getSearchBeginTime(), DatePattern.NORM_DATE_PATTERN);
        DateTime start = DateUtil.beginOfMonth(dateTime);
        DateTime end = DateUtil.endOfMonth(dateTime);

        List<String> ids = csCommTerminalFeignClient.getLineIdsByUser(RequestUtil.getUserIndex()).getData();
        if(CollUtil.isEmpty(ids)){
            return Collections.emptyList();
        }
        List<DateTime> rangList = DateUtil.rangeToList(start,end, DateField.DAY_OF_MONTH);
        List<CsEventPO> csEventPOList = csEventPOService.lambdaQuery().select(CsEventPO::getId,CsEventPO::getTag,CsEventPO::getStartTime,CsEventPO::getPersistTime,CsEventPO::getAmplitude)
                .between(CsEventPO::getStartTime, start, end)
                .eq(CsEventPO::getType, CsEventEnum.EVENT_TYPE.getCode())
                .in(CsEventPO::getTag, EVENT_TAGS)
                .in(CsEventPO::getLineId,ids)
                .list();

        if(CollUtil.isEmpty(csEventPOList)){
            return rangList.stream().map(item->{
                EventStatisticVO eventStatisticVO = new EventStatisticVO();
                eventStatisticVO.setName(DateUtil.format(item,DatePattern.NORM_DATE_PATTERN));
                return eventStatisticVO;
            }).collect(Collectors.toList());
        }

        Map<String, Map<String, Long>>  map = csEventPOList.stream().collect(Collectors.groupingBy(item-> DateUtil.format(item.getStartTime(),DatePattern.NORM_DATE_PATTERN),Collectors.groupingBy(
                CsEventPO::getTag,
                Collectors.counting()
        )));

        return rangList.stream().map(item->{
            EventStatisticVO eventStatisticVO = new EventStatisticVO();
            String day = DateUtil.format(item,DatePattern.NORM_DATE_PATTERN);
            eventStatisticVO.setName(day);

            Map<String,Long> itemMap  = map.getOrDefault(day,Collections.emptyMap());
            eventStatisticVO.setEventDown(itemMap.getOrDefault(EVT_SYS_DIPSTR.getCode(), 0L).intValue());
            eventStatisticVO.setEventOff(itemMap.getOrDefault(EVT_SYS_INTRSTR.getCode(), 0L).intValue());
            eventStatisticVO.setEventUp(itemMap.getOrDefault(EVT_SYS_SWLSTR.getCode(), 0L).intValue());
            return eventStatisticVO;
        }).collect(Collectors.toList());
    }

    @Override
    public EventCoordsVO getEventCoords(EventStatisticParam baseParam) {
        EventCoordsVO eventCoordsVO = new EventCoordsVO();
        // 初始化结果列表
        List<EventCoordsVO.inner> result = new ArrayList<>(50);
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 5; j++) {
                result.add(new EventCoordsVO.inner(i, j, 0));
            }
        }
        List<String> ids = csCommTerminalFeignClient.getLineIdsByUser(RequestUtil.getUserIndex()).getData();
        if(CollUtil.isEmpty(ids)){
            return eventCoordsVO;
        }

        //查询监测点未处理暂态事件
        DateTime start = DateUtil.beginOfDay(DateUtil.parse(baseParam.getSearchBeginTime()));
        DateTime end = DateUtil.endOfDay(DateUtil.parse(baseParam.getSearchEndTime()));
        List<CsEventPO> events = csEventPOService.lambdaQuery().select(CsEventPO::getId,CsEventPO::getTag,CsEventPO::getStartTime,CsEventPO::getPersistTime,CsEventPO::getAmplitude)
                .between(CsEventPO::getStartTime, start, end)
                .eq(CsEventPO::getType, CsEventEnum.EVENT_TYPE.getCode())
                .in(CsEventPO::getTag, EVENT_TAGS)
                .in(CsEventPO::getLineId,ids)
                .list();

       // 定义幅值和持续时间的分类规则
        Map<Double, Integer> amplitudeRanges = ImmutableMap.<Double, Integer>builder()
                .put(0.0, 0)
                .put(10.0, 1)
                .put(20.0, 2)
                .put(30.0, 3)
                .put(40.0, 4)
                .put(50.0, 5)
                .put(60.0, 6)
                .put(70.0, 7)
                .put(80.0, 8)
                .put(90.0, 9)
                // 默认分类
                .put(Double.MAX_VALUE, 10)
                .build();

        // 持续时间分类规则
        Map<Double, Integer> persistTimeRanges = ImmutableMap.<Double, Integer>builder()
                .put(0.01, 0)
                .put(0.1, 1)
                .put(1.0, 2)
                .put(10.0, 3)
                .put(1000.0, 4)
                .build();

        // 时间段分类规则
        Map<Double, Integer> startTimeRanges = ImmutableMap.<Double, Integer>builder()
                .put(8.0, 0)
                .put(12.0, 1)
                .put(14.0, 2)
                .put(18.0, 3)
                .put(24.0, 4)
                .build();

        // 分类事件
        // 0:down, 1:off, 2:up
        List<Integer>[] timeSegmentStats = new List[3];
        for (int i = 0; i < timeSegmentStats.length; i++) {
            // 创建独立的 ArrayList，每个都有初始值 0
            timeSegmentStats[i] = new ArrayList<>();
            // 5个时间段
            for (int j = 0; j < 5; j++) {
                timeSegmentStats[i].add(0);
            }
//            timeSegmentStats[i] = IntStream.range(0, 5).boxed().collect(Collectors.toList());
        }
        for (CsEventPO event : events) {
            double amplitude = event.getAmplitude();
            double persistTimeSec = event.getPersistTime();

            // 确定幅值分类
            int amplitudeGroup = getRangeGroup(amplitudeRanges, amplitude);
            // 确定持续时间分类（幅值分类作为行，持续时间分类作为列）
            int persistTimeGroup = getRangeGroup(persistTimeRanges, persistTimeSec);

            // 计算索引：行 = 幅值分组，列 = 持续时间分组
            int row = amplitudeGroup;
            int col = persistTimeGroup;
            // 10行 x 5列的网格
            int index = row * 5 + col;

            if (index >= 0 && index < result.size()) {
                EventCoordsVO.inner inner = result.get(index);
                inner.setZ(inner.getZ() + 1);
            }

            int hour = event.getStartTime().getHour() + 1;
            int startTimeGroup = getRangeGroup(startTimeRanges, hour);

            String tag = event.getTag();
            if (EVT_SYS_DIPSTR.getCode().equals(tag)) {
                timeSegmentStats[0].set(startTimeGroup, timeSegmentStats[0].get(startTimeGroup) + 1);
            } else if (EVT_SYS_INTRSTR.getCode().equals(tag)) {
                timeSegmentStats[1].set(startTimeGroup, timeSegmentStats[1].get(startTimeGroup) + 1);
            } else if (EVT_SYS_SWLSTR.getCode().equals(tag)) {
                timeSegmentStats[2].set(startTimeGroup, timeSegmentStats[2].get(startTimeGroup) + 1);
            }
        }
        eventCoordsVO.setInnerList(result);
        List<String> xList = Stream.of("0时-8时","8时-12时","12时-14时","14时-18时","18时-24时").collect(Collectors.toList());

        List<EventCoordsVO.TagObj> trendList = Arrays.asList(
                new EventCoordsVO.TagObj(EVT_SYS_DIPSTR.getCode(),
                        timeSegmentStats[0]),
                new EventCoordsVO.TagObj(EVT_SYS_INTRSTR.getCode(),
                        timeSegmentStats[1]),
                new EventCoordsVO.TagObj(EVT_SYS_SWLSTR.getCode(),
                        timeSegmentStats[2])
        );
        eventCoordsVO.setXList(xList);
        eventCoordsVO.setTrendList(trendList);
        return eventCoordsVO;
    }

    @Override
    public List<Map<String, Object>> getEventDirectionData(EventStatisticParam baseParam) {
        List<Map<String,Object>> list = new ArrayList<>();

        Map<String,Object> load = new HashMap<>();
        load.put("source", "load");
        Map<String,Object> grid = new HashMap<>();
        grid.put("source", "grid");

        //获取当前用户拥有的监测点
        List<CsLinePO> poList = csLineFeignClient.getSimpleLine().getData();
        if (CollUtil.isEmpty(poList)) {
            load.put("times", 0);
            grid.put("times", 0);
        } else {
            List<String> ids = poList.stream().map(CsLinePO::getLineId).collect(Collectors.toList());
            //根据监测点获取暂态事件
            //安全处理空列表
            List<RmpEventDetailPO> events = Optional.ofNullable(selectEventByIds(baseParam, ids))
                    .orElse(Collections.emptyList());
            long lowerCount = events.stream()
                    .filter(event -> event != null && "Lower".equalsIgnoreCase(event.getSagsource()))
                    .count();
            long upperCount = events.stream()
                    .filter(event -> event != null && "Upper".equalsIgnoreCase(event.getSagsource()))
                    .count();
            load.put("times", (int) lowerCount);
            grid.put("times", (int) upperCount);
        }
        list.add(load);
        list.add(grid);
        return list;
    }

    //根据监测点获取暂态事件
    public List<RmpEventDetailPO> selectEventByIds(EventStatisticParam param, List<String> ids) {
        LambdaQueryWrapper<RmpEventDetailPO> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(RmpEventDetailPO::getMeasurementPointId,ids)
                .between(RmpEventDetailPO::getStartTime,param.getSearchBeginTime(),param.getSearchEndTime());
        return wlRmpEventDetailMapper.selectList(wrapper);
    }


    /**
     * 根据范围映射表获取分组
     */
    private int getRangeGroup(Map<Double, Integer> ranges, double value) {
        for (Map.Entry<Double, Integer> entry : ranges.entrySet()) {
            if (value <= entry.getKey()) {
                return entry.getValue();
            }
        }
        // 默认返回最大分组
        return ranges.size() - 1;
    }


}
