package com.njcn.csdevice.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.njcn.csdevice.mapper.CsEquipmentDeliveryMapper;
import com.njcn.csdevice.param.LineCountEvaluateParam;
import com.njcn.csdevice.pojo.dto.PqsCommunicateDto;
import com.njcn.csdevice.pojo.po.CsEquipmentDeliveryPO;
import com.njcn.csdevice.service.ICsCommunicateService;
import com.njcn.influx.imapper.PqsCommunicateMapper;
import com.njcn.influx.pojo.po.PqsCommunicate;
import com.njcn.influx.query.InfluxQueryWrapper;
import com.njcn.influx.utils.InfluxDbUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.influxdb.InfluxDB;
import org.influxdb.dto.BatchPoints;
import org.influxdb.dto.Point;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * @Description:
 * @Author: wr
 * @Date: 2025/3/7 10:13
 */
@Slf4j
@Service("InfluxdbCsCommunicateServiceImpl")
@AllArgsConstructor
public class InfluxdbCsCommunicateServiceImpl implements ICsCommunicateService {

    private final InfluxDbUtils influxDbUtils;
    private final CsEquipmentDeliveryMapper csEquipmentDeliveryMapper;
    @Resource
    private PqsCommunicateMapper pqsCommunicateMapper;
    private final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());

    @Override
    public List<PqsCommunicateDto> getRawDataLatest(LineCountEvaluateParam lineParam) {
        List<PqsCommunicateDto> result = new ArrayList<>();
        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(PqsCommunicate.class);
        influxQueryWrapper.regular(PqsCommunicate::getDevId, lineParam.getLineId())
                .select(PqsCommunicate::getTime)
                .select(PqsCommunicate::getDevId)
                .select(PqsCommunicate::getDescription)
                .select(PqsCommunicate::getType)
                .timeDesc()
                .limit(1);
        List<PqsCommunicate> list = pqsCommunicateMapper.selectByQueryWrapper(influxQueryWrapper);
        list.forEach(item -> {
            PqsCommunicateDto dto = new PqsCommunicateDto();
            BeanUtils.copyProperties(item, dto);
            dto.setTime(DATE_TIME_FORMATTER.format(item.getTime()));
            result.add(dto);
        });
        return result;
    }

    @Override
    public List<PqsCommunicateDto> getRawDataOne(LineCountEvaluateParam lineParam) {
        List<PqsCommunicateDto> result = new ArrayList<>();
        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(PqsCommunicate.class);
        influxQueryWrapper.regular(PqsCommunicate::getDevId, lineParam.getLineId())
                .select(PqsCommunicate::getTime)
                .select(PqsCommunicate::getDevId)
                .select(PqsCommunicate::getDescription)
                .select(PqsCommunicate::getType)
                .timeAsc()
                .limit(1);
        List<PqsCommunicate> list = pqsCommunicateMapper.selectByQueryWrapper(influxQueryWrapper);
        list.forEach(item -> {
            PqsCommunicateDto dto = new PqsCommunicateDto();
            BeanUtils.copyProperties(item, dto);
            dto.setTime(DATE_TIME_FORMATTER.format(item.getTime()));
            result.add(dto);
        });
        return result;
    }

    /**
     * @Description: 获取时间段内的数据
     * @Param:
     * @return: java.util.List<com.njcn.dataProcess.dto.DataVDTO>
     * @Author: clam
     * @Date: 2025/02/24
     */
    @Override
    public List<PqsCommunicateDto> getRawData(LineCountEvaluateParam lineParam) {
        List<PqsCommunicateDto> result = new ArrayList<>();
        List<PqsCommunicate> list = getPqsCommunicateData(lineParam);

        if (CollectionUtil.isNotEmpty(list)) {
            result = processWithData(list, lineParam);
        } else {
            result = processWithoutData(lineParam);
        }

        return result;
    }

    @Override
    public List<PqsCommunicateDto> getRawData2(LineCountEvaluateParam lineParam) {
        List<PqsCommunicateDto> result = new ArrayList<>();
        List<PqsCommunicate> list = getPqsCommunicateData(lineParam);
        if (CollectionUtil.isNotEmpty(list)) {
            list.forEach(item -> {
                result.add(convertToDto(item));
            });
        }
        return result;
    }

    /**
     * 处理有数据的情况
     */
    private List<PqsCommunicateDto> processWithData(List<PqsCommunicate> list, LineCountEvaluateParam lineParam) {
        List<PqsCommunicateDto> result = new ArrayList<>();
        int lastIndex = list.size() - 1;

        // 获取第一条数据，补充0点数据（状态与第一条相反）
        PqsCommunicate firstItem = list.get(0);
        PqsCommunicateDto firstData = new PqsCommunicateDto();
        firstData.setTime(lineParam.getStartTime() + " 00:00:00");
        firstData.setDevId(lineParam.getLineId().get(0));
        if (Objects.equals(firstItem.getType(), 0)) {
            firstData.setType(1);
            firstData.setDescription("通讯正常");
        } else {
            firstData.setType(0);
            firstData.setDescription("通讯中断");
        }
        result.add(firstData);

        for (int i = 0; i < list.size(); i++) {
            PqsCommunicate item = list.get(i);
            PqsCommunicateDto dto = convertToDto(item);
            result.add(dto);

            // 如果是最后一组数据，补充结束数据点
            if (i == lastIndex) {
                PqsCommunicateDto endData = createEndDataDto(item, lineParam);
                result.add(endData);
            }
        }
        return result;
    }


    /**
     * 处理无数据的情况
     */
    private List<PqsCommunicateDto> processWithoutData(LineCountEvaluateParam lineParam) {
        LocalDateTime endTime = getEndDateTime(lineParam.getEndTime());
        LocalDateTime currentTime = LocalDateTime.now();

        // 判断 endTime 是否早于最早入库时间
        List<PqsCommunicateDto> firstData = getRawDataFirst(lineParam);
        if (CollectionUtil.isNotEmpty(firstData)) {
            LocalDateTime firstTime = parseDateTime(firstData.get(0).getTime());
            if (endTime.isBefore(firstTime)) {
                return Collections.emptyList();
            }
        }

        // 判断 startTime 是否晚于当前时间
        LocalDateTime startTime = getStartDateTime(lineParam.getStartTime());
        if (startTime.isAfter(currentTime)) {
            return Collections.emptyList();
        }

        // 获取最新数据并创建起始和结束点
        List<PqsCommunicateDto> latestData = getRawDataLatest(lineParam);
        if (CollectionUtil.isEmpty(latestData)) {
            return Collections.emptyList();
        }

        return createStartAndEndDtos(latestData.get(0), lineParam);
    }

    /**
     * 创建起始和结束数据点
     */
    private List<PqsCommunicateDto> createStartAndEndDtos(PqsCommunicateDto templateDto, LineCountEvaluateParam lineParam) {
        List<PqsCommunicateDto> result = new ArrayList<>();

        // 创建起始点
        PqsCommunicateDto startDto = new PqsCommunicateDto();
        copyDtoProperties(templateDto, startDto);
        startDto.setTime(lineParam.getStartTime() + " 00:00:00");
        result.add(startDto);

        // 创建结束点
        PqsCommunicateDto endDto = new PqsCommunicateDto();
        copyDtoProperties(templateDto, endDto);
        endDto.setTime(getEndTimeString(lineParam));
        result.add(endDto);

        return result;
    }

    /**
     * 创建结束数据点
     */
    private PqsCommunicateDto createEndDataDto(PqsCommunicate item, LineCountEvaluateParam lineParam) {
        PqsCommunicateDto endData = new PqsCommunicateDto();
        endData.setTime(getEndTimeString(lineParam));
        endData.setDevId(lineParam.getLineId().get(0));
        endData.setType(item.getType());
        endData.setDescription(item.getDescription());
        return endData;
    }

    /**
     * 转换实体为DTO
     */
    private PqsCommunicateDto convertToDto(PqsCommunicate item) {
        PqsCommunicateDto dto = new PqsCommunicateDto();
        BeanUtils.copyProperties(item, dto);
        dto.setTime(DATE_TIME_FORMATTER.format(item.getTime()));
        return dto;
    }

    /**
     * 复制DTO属性
     */
    private void copyDtoProperties(PqsCommunicateDto source, PqsCommunicateDto target) {
        target.setDevId(source.getDevId());
        target.setType(source.getType());
        target.setDescription(source.getDescription());
    }

    /**
     * 获取结束时间字符串
     */
    private String getEndTimeString(LineCountEvaluateParam lineParam) {
        LocalDateTime currentTime = LocalDateTime.now();
        LocalDateTime endTime = getEndDateTime(lineParam.getEndTime());

        if (endTime.isBefore(currentTime)) {
            return lineParam.getEndTime() + " 23:59:59";
        } else {
            return currentTime.format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN));
        }
    }

    /**
     * 解析日期时间字符串
     */
    private LocalDateTime parseDateTime(String dateTimeStr) {
        return LocalDateTime.parse(dateTimeStr, DATE_TIME_FORMATTER);
    }

    /**
     * 获取开始时间
     */
    private LocalDateTime getStartDateTime(String startDate) {
        return parseDateTime(startDate + " 00:00:00");
    }

    /**
     * 获取结束时间
     */
    private LocalDateTime getEndDateTime(String endDate) {
        return parseDateTime(endDate + " 23:59:59");
    }

    @Override
    public List<PqsCommunicateDto> getRawDataEnd(LineCountEvaluateParam lineParam) {
        List<PqsCommunicateDto> result = new ArrayList<>();
        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(PqsCommunicate.class);
        influxQueryWrapper.regular(PqsCommunicate::getDevId, lineParam.getLineId())
                .select(PqsCommunicate::getTime)
                .select(PqsCommunicate::getDevId)
                .select(PqsCommunicate::getDescription)
                .select(PqsCommunicate::getType)
                .between(PqsCommunicate::getTime, lineParam.getStartTime(), lineParam.getEndTime())
                .timeDesc()
                .limit(1);
        List<PqsCommunicate> list = pqsCommunicateMapper.selectByQueryWrapper(influxQueryWrapper);
        list.forEach(item -> {
            PqsCommunicateDto dto = new PqsCommunicateDto();
            BeanUtils.copyProperties(item, dto);
            dto.setTime(DATE_TIME_FORMATTER.format(item.getTime()));
            result.add(dto);
        });
        return result;
    }

    @Override
    public void insertion(PqsCommunicateDto pqsCommunicateDto) {
        //根据NDID获取装置信息
        LambdaQueryWrapper<CsEquipmentDeliveryPO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(CsEquipmentDeliveryPO::getNdid,pqsCommunicateDto.getDevId()).ne(CsEquipmentDeliveryPO::getRunStatus,0);
        CsEquipmentDeliveryPO po = csEquipmentDeliveryMapper.selectOne(lambdaQueryWrapper);

        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(PqsCommunicate.class);
        influxQueryWrapper.eq(PqsCommunicate::getDevId,po.getId()).timeDesc().limit(1);
        List<PqsCommunicate> pqsCommunicates = pqsCommunicateMapper.selectByQueryWrapper(influxQueryWrapper);
        //如果不存数据或者状态不一样则插入数据
       if(CollectionUtils.isEmpty(pqsCommunicates) || !Objects.equals(pqsCommunicates.get(0).getType(),pqsCommunicateDto.getType())){
           Map<String, String> tags = new HashMap<>();
           tags.put("dev_id",po.getId());
           Map<String,Object> fields = new HashMap<>();
           fields.put("type",pqsCommunicateDto.getType());
           fields.put("description",pqsCommunicateDto.getDescription());
           long time = LocalDateTime.parse(pqsCommunicateDto.getTime(),  DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
           Point point = influxDbUtils.pointBuilder("pqs_communicate", time, TimeUnit.MILLISECONDS, tags, fields);
           BatchPoints batchPoints = BatchPoints.database(influxDbUtils.getDbName()).retentionPolicy("").consistency(InfluxDB.ConsistencyLevel.ALL).build();
           batchPoints.point(point);
           influxDbUtils.batchInsert(influxDbUtils.getDbName(), "", InfluxDB.ConsistencyLevel.ALL, TimeUnit.MILLISECONDS, Collections.singletonList(batchPoints.lineProtocol()));
       }
    }


    public List<PqsCommunicate> getPqsCommunicateData(LineCountEvaluateParam lineParam) {
        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(PqsCommunicate.class);
        influxQueryWrapper.regular(PqsCommunicate::getDevId, lineParam.getLineId())
                .select(PqsCommunicate::getTime)
                .select(PqsCommunicate::getDevId)
                .select(PqsCommunicate::getDescription)
                .select(PqsCommunicate::getType)
                .between(PqsCommunicate::getTime, lineParam.getStartTime() + " 00:00:00", lineParam.getEndTime() + " 23:59:59")
                .timeAsc();
        return pqsCommunicateMapper.selectByQueryWrapper(influxQueryWrapper);
    }

    public List<PqsCommunicateDto> getRawDataFirst(LineCountEvaluateParam lineParam) {
        List<PqsCommunicateDto> result = new ArrayList<>();
        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(PqsCommunicate.class);
        influxQueryWrapper.regular(PqsCommunicate::getDevId, lineParam.getLineId())
                .select(PqsCommunicate::getTime)
                .select(PqsCommunicate::getDevId)
                .select(PqsCommunicate::getDescription)
                .select(PqsCommunicate::getType)
                .timeAsc()
                .limit(1);
        List<PqsCommunicate> list = pqsCommunicateMapper.selectByQueryWrapper(influxQueryWrapper);
        list.forEach(item -> {
            PqsCommunicateDto dto = new PqsCommunicateDto();
            BeanUtils.copyProperties(item, dto);
            dto.setTime(DATE_TIME_FORMATTER.format(item.getTime()));
            result.add(dto);
        });
        return result;
    }
}
