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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
//import com.github.tocrhz.mqtt.publisher.MqttPublisher;
import com.njcn.advance.api.EventCauseFeignClient;
import com.njcn.advance.pojo.dto.EventAnalysisDTO;
import com.njcn.common.utils.PubUtils;
import com.njcn.device.biz.commApi.CommTerminalGeneralClient;
import com.njcn.device.pq.api.DeptLineFeignClient;
import com.njcn.device.pq.api.LineFeignClient;
import com.njcn.device.pq.pojo.po.DeptLine;
import com.njcn.device.pq.pojo.vo.AreaLineInfoVO;
import com.njcn.device.pq.pojo.vo.LineDetailDataVO;
import com.njcn.event.common.mapper.RmpEventDetailMapper;
import com.njcn.event.common.websocket.WebSocketServer;
import com.njcn.event.pojo.vo.SendEventVO;
import com.njcn.event.utils.EventUtil;
import com.njcn.event.pojo.dto.EventDeatilDTO;
import com.njcn.event.pojo.po.EventDetail;
import com.njcn.event.pojo.po.RmpEventDetailPO;
import com.njcn.event.common.service.EventDetailService;
import com.njcn.influx.utils.InfluxDbUtils;
import com.njcn.system.api.DicDataFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.pojo.po.DictData;
import com.njcn.user.api.DeptFeignClient;
import com.njcn.user.api.UserFeignClient;
import com.njcn.user.pojo.po.Dept;
import com.njcn.user.pojo.po.User;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.influxdb.dto.QueryResult;
import org.influxdb.impl.InfluxDBResultMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * @author denghuajun
 * @version 1.0.0
 * @date 2022/3/16
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class EventDetailServiceImpl extends ServiceImpl<RmpEventDetailMapper, RmpEventDetailPO> implements EventDetailService {

    private final InfluxDbUtils influxDbUtils;
    private final DicDataFeignClient dicDataFeignClient;
//    private final MqttPublisher publisher;

    private final WebSocketServer webSocketServer;
    private final CommTerminalGeneralClient commTerminalGeneralClient;
    private final LineFeignClient lineFeignClient;
    private final DeptLineFeignClient deptLineFeignClient;
    private final DeptFeignClient deptFeignClient;
    private final UserFeignClient userFeignClient;
    @Resource(name="asyncExecutor")
    private Executor executor;
    private final EventCauseFeignClient eventCauseFeignClient;
    @Override
    public List<EventDetail> getEventDetailData(String id, String startTime, String endTime) {
        //数据暂降查询
        List<RmpEventDetailPO> info = this.list(new LambdaQueryWrapper<RmpEventDetailPO>()
                .eq(RmpEventDetailPO::getMeasurementPointId, id)
                .ge(StrUtil.isNotBlank(startTime), RmpEventDetailPO::getStartTime,DateUtil.beginOfDay(DateUtil.parse(startTime)))
                .le(StrUtil.isNotBlank(endTime), RmpEventDetailPO::getStartTime, DateUtil.endOfDay(DateUtil.parse(endTime)))
        );
        return  BeanUtil.copyToList(info, EventDetail.class);
    }

    @Override
    public EventDetail getEventDetailByTime(String id, String time) {
        EventDetail eventDetail = new EventDetail();
        //组装sql语句
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("time = '").append(time).append("' and ").append("line_id ='").append(id).append("' limit 1 tz('Asia/Shanghai')");

        String sql = "select * from pqs_eventdetail where " + stringBuilder;
        //获取暂降事件
        QueryResult result = influxDbUtils.query(sql);
        InfluxDBResultMapper influxDBResultMapper = new InfluxDBResultMapper();
        if (!Objects.isNull(result)) {
            List<EventDetail> detailList = influxDBResultMapper.toPOJO(result, EventDetail.class);
            if (!CollectionUtils.isEmpty(detailList)) {
                eventDetail = detailList.get(0);
            }
        }
        return eventDetail;
    }

    @Override
    public List<EventDetail> getEventDetail(List<String> lineIndexes, String startTime, String endTime) {
        List<RmpEventDetailPO> info = this.list(new LambdaQueryWrapper<RmpEventDetailPO>()
                .in(RmpEventDetailPO::getMeasurementPointId, lineIndexes)
                .ge(StrUtil.isNotBlank(startTime), RmpEventDetailPO::getStartTime,DateUtil.beginOfDay(DateUtil.parse(startTime)))
                .le(StrUtil.isNotBlank(endTime), RmpEventDetailPO::getStartTime, DateUtil.endOfDay(DateUtil.parse(endTime)))
                .orderByDesc(RmpEventDetailPO::getStartTime)
        );
        return  BeanUtil.copyToList(info, EventDetail.class);

//        //组装sql语句
//        StringBuilder stringBuilder = new StringBuilder();
//        stringBuilder.append("time >= '").append(DateUtil.beginOfDay(DateUtil.parse(startTime))).append("' and ").append("time <= '").append(DateUtil.endOfDay(DateUtil.parse(endTime))).append("' and (");
//        for (int i = 0; i < lineIndexes.size(); i++) {
//            if (lineIndexes.size() - i != 1) {
//                stringBuilder.append("line_id ='").append(lineIndexes.get(i)).append("' or ");
//            } else {
//                stringBuilder.append("line_id ='").append(lineIndexes.get(i)).append("' ");
//            }
//        }
//        stringBuilder.append(") and (");
//        for (int i = 0; i < waveType.size(); i++) {
//            if (waveType.size() - i != 1) {
//                stringBuilder.append("wave_type ='").append(waveType.get(i)).append("' or ");
//            } else {
//                stringBuilder.append("wave_type ='").append(waveType.get(i)).append("' ");
//            }
//        }
//        stringBuilder.append(") order by time desc tz('Asia/Shanghai");
//        //sql语句
//        String sql = "SELECT * FROM pqs_eventdetail WHERE " + stringBuilder;
//
//        //结果集
//        QueryResult result = influxDbUtils.query(sql);
//        //结果集映射到对象中
//        InfluxDBResultMapper influxDBResultMapper = new InfluxDBResultMapper();
//        List<EventDetail> eventDetailList = influxDBResultMapper.toPOJO(result, EventDetail.class);
//        return eventDetailList;
    }

    @Override
    public List<EventDetail> getEventDetailLimit(List<String> lineIndexes, String startTime, String endTime, Integer pageSize, Integer pageNum, List<String> waveType) {
        Page<RmpEventDetailPO> pageInfo = this.page(new Page<>(pageNum, pageSize), new LambdaQueryWrapper<RmpEventDetailPO>()
                .in(RmpEventDetailPO::getMeasurementPointId, lineIndexes)
                .ge(StrUtil.isNotBlank(startTime), RmpEventDetailPO::getStartTime,DateUtil.beginOfDay(DateUtil.parse(startTime)))
                .le(StrUtil.isNotBlank(endTime), RmpEventDetailPO::getStartTime, DateUtil.endOfDay(DateUtil.parse(endTime)))
        );
        return BeanUtil.copyToList(pageInfo.getRecords(), EventDetail.class);
    }

    @Override
//    @Transactional(rollbackFor = Exception.class)
    public Boolean addEventDetail(EventDeatilDTO deatilDTO) {
        RmpEventDetailPO one = this.lambdaQuery().eq(RmpEventDetailPO::getLineId, deatilDTO.getMonitorId()).eq(RmpEventDetailPO::getStartTime, deatilDTO.getStartTime()).one();
        RmpEventDetailPO rmpEventDetailPO = new RmpEventDetailPO();

        if(Objects.nonNull(one)){
            BeanUtils.copyProperties(one,rmpEventDetailPO);

        }
//        rmpEventDetailPO.setMeasurementPointId(deatilDTO.getMonitorId());
        rmpEventDetailPO.setLineId(deatilDTO.getMonitorId());
        DictData data = dicDataFeignClient.getDicDataByCode(eventTypeReflection(deatilDTO.getEventType())).getData();
        rmpEventDetailPO.setEventType(data.getId());
        rmpEventDetailPO.setStartTime(deatilDTO.getStartTime());
        rmpEventDetailPO.setDuration(deatilDTO.getDuration()/1000);
        rmpEventDetailPO.setFeatureAmplitude(deatilDTO.getAmplitude()/100);
        rmpEventDetailPO.setPhase(deatilDTO.getPhase());
        rmpEventDetailPO.setWavePath(deatilDTO.getWavePath());
        if(StringUtils.isEmpty(deatilDTO.getWavePath())){
            rmpEventDetailPO.setFileFlag(0);
        }else {
            rmpEventDetailPO.setFileFlag(1);
        }
        //默认给0，经过高级算法在赋值
        rmpEventDetailPO.setDealFlag(0);
        rmpEventDetailPO.setEventDescribe(" ");
        //如果不为空，说明是二次上传波形文件了;



        String severity = EventUtil.getYzd(deatilDTO.getDuration().floatValue(),(deatilDTO.getAmplitude().floatValue()/100));
        rmpEventDetailPO.setSeverity(Double.valueOf(severity));
        rmpEventDetailPO.setCreateTime(LocalDateTime.now());

        if(Objects.nonNull(one)){
            rmpEventDetailPO.setEventId(one.getEventId());

            this.updateById(rmpEventDetailPO);
        } else {
            this.save(rmpEventDetailPO);
            //推送MQTT消息给前端，用于展示告警消息和弹窗
            //如果是补招数据，如果事件的时间跟当前时间相差1小时，则不出弹窗。
            Duration duration = Duration.between(rmpEventDetailPO.getStartTime(), LocalDateTime.now());
            if (duration.toHours() <= 1 && !duration.isNegative()) {
                pushEvent(rmpEventDetailPO);
            }
        }
        //异步调用高级算法
        if (!StringUtils.isEmpty(rmpEventDetailPO.getWavePath())) {
            CompletableFuture.runAsync(() -> {
                // 异步任务内执行分析及更新
                analyzeAndUpdateEvent(rmpEventDetailPO.getEventId(), rmpEventDetailPO.getLineId(), rmpEventDetailPO.getWavePath());
            }, executor).exceptionally(ex -> {
                // exceptionally 也可以捕获异常，但内部已 try-catch，这里仅做兜底日志
                log.error("异步任务未预期的异常, eventId: {}", rmpEventDetailPO.getEventId(), ex);
                return null;
            });
        }

        return true;
    }

    private void analyzeAndUpdateEvent(String eventId, String lineId, String wavePath) {
        // 重新查询实体，避免跨线程持久化对象问题
        RmpEventDetailPO po = this.getById(eventId);
        if (po == null) {
            log.warn("异步分析时事件记录不存在, eventId: {}", eventId);
            return;
        }

        try {
            // Feign 调用链
            LineDetailDataVO lineDetailData = lineFeignClient.getLineDetailData(lineId).getData();
            String ip = lineDetailData.getIp();
            EventAnalysisDTO eventAnalysisDTO = new EventAnalysisDTO();
            eventAnalysisDTO.setIp(ip);
            eventAnalysisDTO.setWaveName(wavePath);

            EventAnalysisDTO result = eventCauseFeignClient.analysisCauseAndType(eventAnalysisDTO).getData();

            // 转换结果
            String reasonCode, typeCode;
            if (Objects.isNull(result.getCause())) {
                reasonCode = reasonReflection(0);
            } else {
                reasonCode = reasonReflection(result.getCause());
            }
            if (Objects.isNull(result.getType())) {
                typeCode = advanceTypeReflection(10);
            } else {
                typeCode = advanceTypeReflection(result.getType());
            }

            DictData advancereason = dicDataFeignClient.getDicDataByCode(reasonCode).getData();
            DictData advanceType = dicDataFeignClient.getDicDataByCode(typeCode).getData();

            po.setAdvanceReason(advancereason.getId());
            po.setAdvanceType(advanceType.getId());
            if (Objects.equals(result.getCauseFlag(), 1) && Objects.equals(result.getTypeFlag(), 1)) {
                po.setDealFlag(1);
            } else {
                po.setDealFlag(0);
            }
            // 更新数据库
            this.updateById(po);
        } catch (Exception e) {
            // 关键点：所有异常被捕获并记录日志，不会抛出到主线程
            log.error("异步分析失败, eventId={}, wavePath={}", eventId, wavePath, e);
            // 可选：设置一个错误标志，避免一直处于未分析状态
            po.setDealFlag(0);
            this.updateById(po);
        }
    }

    private String advanceTypeReflection(Integer type) {
        String result = DicDataEnum.TYPE_REST.getCode();
        switch (type) {
            case 0:
                result = DicDataEnum.INTERPHASE_BC.getCode();
                break;
            case 1:
                result = DicDataEnum.PHASE_C.getCode();
                break;
            case 2:
                result = DicDataEnum.INTERPHASE_AC.getCode();
                break;
            case 3:
                result = DicDataEnum.PHASE_A.getCode();
                break;
            case 4:
                result = DicDataEnum.INTERPHASE_AB.getCode();
                break;
            case 5:
                result = DicDataEnum.PHASE_B.getCode();
                break;
            case 6:
                result = DicDataEnum.GROUND_BC.getCode();
                break;
            case 7:
                result = DicDataEnum.GROUND_AC.getCode();
                break;
            case 8:
                result = DicDataEnum.GROUND_AB.getCode();
                break;
            case 9:
                result = DicDataEnum.GROUND_ABC.getCode();
                break;
            case 10:
                result = DicDataEnum.TYPE_REST.getCode();
                break;
        }
        return result;
    }

    private String reasonReflection(Integer cause) {
        String result =DicDataEnum.RESON_REST.getCode();
        switch (cause) {
            case 0:
                result = DicDataEnum.RESON_REST.getCode();
                break;
            case 1:
                result = DicDataEnum.SHORT_TROUBLE.getCode();
                break;
            case 2:
                result = DicDataEnum.TRANSFORMER_EXCITATION.getCode();
                break;
            case 3:
                result = DicDataEnum.LARGE_INDUCTION.getCode();
                break;
            case 4:
                result = DicDataEnum.VOLTAGE_DISTURBANCE.getCode();
                break;
        }
        return result;
    }

    private void pushEvent(RmpEventDetailPO po) {
        String id = po.getLineId();
        //根据监测点获取部门
        List<DeptLine> deptLineList = deptLineFeignClient.getDeptListByLineId(id).getData();
        if (CollUtil.isNotEmpty(deptLineList)) {
            Set<String> dept = new HashSet<>();
            deptLineList.forEach(item->{
                String deptLineId = item.getId();
                dept.add(deptLineId);
                //获取上级部门
                Dept deptInfo = deptFeignClient.getDeptById(deptLineId).getData();
                String[] idsArray = deptInfo.getPids().split(",");
                dept.addAll(Arrays.asList(idsArray));
            });
            List<String> deptList = dept.stream().collect(Collectors.toList());

            List<User> data = userFeignClient.getUserInfoByDeptIds(deptList).getData();
            SendEventVO vo = new SendEventVO();
            vo.setDeptList(dept);
            vo.setTime(po.getStartTime());
            vo.setEventValue(BigDecimal.valueOf(po.getFeatureAmplitude()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            vo.setPersistTime(po.getDuration());
            vo.setEventType(po.getEventType());
            vo.setEventReason(po.getAdvanceReason());
            List<AreaLineInfoVO> lineInfoVOList = lineFeignClient.getBaseLineAreaInfo(Collections.singletonList(po.getLineId())).getData();
            vo.setLineName(lineInfoVOList.get(0).getLineName());
            vo.setPowerCompany(lineInfoVOList.get(0).getGdName());
            vo.setSubstation(lineInfoVOList.get(0).getSubName());
            for (User datum : data) {
                webSocketServer.sendMessageToUser(datum.getId(),PubUtils.obj2json(vo));
            }
//            publisher.send("/sendEvent", PubUtils.obj2json(vo), 1, false);
        }
    }

    public String eventTypeReflection(Integer eventType){
        String result ="";
        switch (eventType) {
            case 1210001:
                result = DicDataEnum.VOLTAGE_RISE.getCode();
                break;
            case 1210002:
                result = DicDataEnum.VOLTAGE_DIP.getCode();
                break;
            case 1210003:
                result = DicDataEnum.OTHER.getCode();
                break;
            case 1210004:
                result = DicDataEnum.SHORT_INTERRUPTIONS.getCode();
                break;
        }
        return result;
    }


}
