package com.mvc.service.sthjb;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.mvc.constant.BaseConstant;
import com.mvc.constant.ReviewResultType;
import com.mvc.constant.TaskStatus;
import com.mvc.dao.TPipelineLeakageMapper;
import com.mvc.entity.TAiread;
import com.mvc.entity.TPipelineLeakage;
import com.mvc.entity.TPipelineLeakageExample;
import com.mvc.model.ResPageModel;
import com.mvc.model.StationAccModel;
import com.mvc.model.TAireadModel;
import com.mvc.model.TaskRemoteCfgModel;
import com.mvc.model.sthjb.TPipelineLeakageModel;
import com.mvc.service.copy.TAireadService;
import com.mvc.service.taizhang.StationAccService;
import com.mvc.service.taizhang.TaskRemoteCfgService;
import com.mvc.utils.DateUtil;
import com.mvc.utils.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 管网漏损接口
 */
@Service
@Transactional
public class TPipelineLeakageService {
    private final static Logger log = LoggerFactory.getLogger(TPipelineLeakageService.class);

    @Resource
    private TPipelineLeakageMapper tPipelineLeakageMapper;

    @Resource
    private TaskRemoteCfgService taskRemoteCfgService;

    @Resource
    private TAireadService aireadService;

    @Resource
    private StationAccService stationAccService;

    /**
     * 根据条件分页查询列表
     * @param queryModel
     * @return
     */
    public ResPageModel findAllTPipelineLeakage(TPipelineLeakageModel queryModel) {
        TPipelineLeakageExample example = new TPipelineLeakageExample();
        TPipelineLeakageExample.Criteria criteria = example.createCriteria();
        if (StringUtils.isNotEmpty(queryModel.getCreateMan())) {
            criteria.andCreateManLike(StringUtil.getLikeFormat(queryModel.getCreateMan()));
        }
        if (queryModel.getTaskStatus() != null) {
            criteria.andTaskStatusEqualTo(queryModel.getTaskStatus());
        }
        if (StringUtils.isNotEmpty(queryModel.getUnitCode())) {
            criteria.andUnitCodeEqualTo(queryModel.getUnitCode());
        }
        example.setOrderByClause(" CREATE_TIME desc ");

        Page<Object> pageObject = PageHelper.startPage(queryModel.getPageNum(), queryModel.getPageSize());
        List<TPipelineLeakageModel> pipelineLeakageModels = selectByExample(example);

        ResPageModel resPageModel = new ResPageModel(pipelineLeakageModels, pageObject);
        return resPageModel;
    }

    /**
     * 根据Example查询列表
     * @param example
     * @return
     */
    private List<TPipelineLeakageModel> selectByExample(TPipelineLeakageExample example) {
        List<TPipelineLeakage> tPipelineLeakages = tPipelineLeakageMapper.selectByExample(example);
        List<TPipelineLeakageModel> resList = new ArrayList<>();
        for (TPipelineLeakage TPipelineLeakage : tPipelineLeakages) {
            TPipelineLeakageModel model = new TPipelineLeakageModel();
            BeanUtils.copyProperties(TPipelineLeakage, model);

            StationAccModel accModel = stationAccService.findStationAccByStationCode(TPipelineLeakage.getStationCode());
            if (accModel != null) {
                model.setStationName(accModel.getStationName());
            }
            resList.add(model);
        }
        return resList;
    }

    /**
     * 新增记录
     * @param model
     * @return
     */
    public TPipelineLeakageModel saveTPipelineLeakage(TPipelineLeakageModel model) {
        model.setCreateTime(new Date());
        TPipelineLeakage tPipelineLeakage = new TPipelineLeakage();
        BeanUtils.copyProperties(model, tPipelineLeakage);
        int insert = tPipelineLeakageMapper.insert(tPipelineLeakage);
        model.setId(tPipelineLeakage.getId());
        return model;
    }

    /**
     * 更新记录
     * @param model
     * @return
     */
    public TPipelineLeakageModel updateTPipelineLeakage(TPipelineLeakageModel model) {
        TPipelineLeakage tPipelineLeakage = tPipelineLeakageMapper.selectByPrimaryKey(model.getId());
        BeanUtils.copyProperties(model, tPipelineLeakage);
        int i = tPipelineLeakageMapper.updateByPrimaryKey(tPipelineLeakage);
        return model;
    }

    /**
     * 根据ID删除记录
     * @param id
     * @return
     */
    public int deleteTPipelineLeakage(int id) {
        int i = tPipelineLeakageMapper.deleteByPrimaryKey(id);
        return i;
    }

    /**
     * 根据ID获取Model
     *
     * @param id
     * @return
     */
    public TPipelineLeakageModel findTPipelineLeakageModelById(int id) {
        TPipelineLeakage tPipelineLeakage = tPipelineLeakageMapper.selectByPrimaryKey(id);
        if (tPipelineLeakage != null) {
            TPipelineLeakageModel model = new TPipelineLeakageModel();
            BeanUtils.copyProperties(tPipelineLeakage, model);
            return model;
        }
        return null;
    }

    //创建开始测量的任务
    public void createStartTask() {
        //1.查询未执行的任务
        TPipelineLeakageExample example = new TPipelineLeakageExample();
        TPipelineLeakageExample.Criteria criteria = example.createCriteria();
        criteria.andTaskStatusEqualTo((byte) 0);
        List<TPipelineLeakageModel> tPipelineLeakages = selectByExample(example);

        String tomorrow = DateUtil.formateDate(DateUtil.getOtherDateByCurrentDate(new Date(), 1), "yyyy-MM-dd");
        for (TPipelineLeakageModel tPipelineLeakage : tPipelineLeakages) {
            //2.判断时间是否符合明天要测量的时间
            if (DateUtil.formateDate(tPipelineLeakage.getMeasurementDate(), "yyyy-MM-dd").equals(tomorrow)) {
                handlePipelineLeakageTask(tPipelineLeakage, 1);
                System.out.println("开始测量任务:" + tPipelineLeakage.getStationCode());
            }
        }
    }

    //创建结束测量的任务
    public void createEndTask() {
        //1.查询正在执行的任务
        TPipelineLeakageExample example = new TPipelineLeakageExample();
        TPipelineLeakageExample.Criteria criteria = example.createCriteria();
        criteria.andTaskStatusEqualTo((byte) 1);
        List<TPipelineLeakageModel> tPipelineLeakages = selectByExample(example);
        Date now = new Date();

        for (TPipelineLeakageModel tPipelineLeakage : tPipelineLeakages) {
            Date date1 = DateUtil.getOtherDateByMinute(tPipelineLeakage.getMeasurementDate(), tPipelineLeakage.getMeasurementInterval());
            //未达到结束测量时间
            if (date1.after(now)) {
                continue;
            }
            //达到结束测量时间
            TaskRemoteCfgModel cfgModel = taskRemoteCfgService.findTaskRemoteCfgById(tPipelineLeakage.getPreTaskId());
            if (cfgModel == null) {
                log.info("开始前的配置任务不存在:" + tPipelineLeakage.getStationCode());
                continue;
            }
            //开始前的配置任务执行成功了(理论上此刻结束的读数也已经有了)
            if (cfgModel.getFinishedFlag().equals(TaskStatus.EXECUTED.getCode())) {//
                handlePipelineLeakageTask(tPipelineLeakage, 2);
                log.info("结束测量任务:" + tPipelineLeakage.getStationCode());
            }else{
                log.info("开始前的配置任务未执行成功:" + tPipelineLeakage.getStationCode());
            }
        }
    }

    //手动计算漏损(供前端触发,当修正读数后再次计算)
    @Async
    public void manualCalcPipelineLeakage(int id) {
        TPipelineLeakage tPipelineLeakage = tPipelineLeakageMapper.selectByPrimaryKey(id);
        if (tPipelineLeakage != null) {
            TPipelineLeakageModel model = new TPipelineLeakageModel();
            BeanUtils.copyProperties(tPipelineLeakage, model);
            model = calcPipelineLeakage(model);
            updateTPipelineLeakage(model);
        }
    }

    //计算漏损
    private TPipelineLeakageModel calcPipelineLeakage(TPipelineLeakageModel model) {
        Date startDate = model.getMeasurementDate();
        Date endDate = DateUtil.getOtherDateByMinute(model.getMeasurementDate(), model.getMeasurementInterval());
        List<TAiread> aireads = aireadService.findAllTaireadByStationCodeAndTime(model.getStationCode(), startDate, endDate);
        if (aireads.size() < 2 || aireads.get(0).getSampleTime().compareTo(startDate) != 0 || aireads.get(aireads.size() - 1).getSampleTime().compareTo(endDate) != 0) {
            //读数数据不完整
            model.setTaskStatus((byte) 3);
        } else {
            TAiread airead0 = aireads.get(0);
            TAiread airead1 = aireads.get(aireads.size() - 1);
            BigDecimal value = airead1.getResultValue().subtract(airead0.getResultValue());

            model.setStartUsage(airead0.getResultValue());//起始用量
            model.setStartAireadId(airead0.getId());//记录计算的开始读数ID
            model.setEndAireadId(airead1.getId());//记录计算的结束读数ID
            model.setEndUsage(airead1.getResultValue());//结束用量
            model.setUsageDifference(value);//差额

            //计算漏损率(差额/日均用量*100)
            BigDecimal dailyWaterUsageAvg = calcDailyWaterUsageAvg(model.getStationCode());
            if (dailyWaterUsageAvg != null && dailyWaterUsageAvg.compareTo(BigDecimal.ZERO) > 0) {
                var leakageRate = value.divide(dailyWaterUsageAvg, 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
                model.setLeakageRate(leakageRate.setScale(2, RoundingMode.HALF_UP));
            }
            model.setTaskStatus((byte) 2);
        }
        return model;
        ////updateTPipelineLeakage(model);//在父方法中执行更新
    }

    //根据站代码计算日均用量
    private BigDecimal calcDailyWaterUsageAvg(String stationCode) {
        //按照条例,开始时间应该是当年第一天,结束时间是当日
        LocalDate today = LocalDate.now();
        // 使用TemporalAdjusters获取今年的第一天
        LocalDate firstDayOfYear = today.with(TemporalAdjusters.firstDayOfYear());
        // 将LocalDate转换为Date
        Date firstDayOfYearAsDate = Date.from(firstDayOfYear.atStartOfDay(ZoneId.systemDefault()).toInstant());
        Date endDate = Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());

        //获取该年的所有数据(正序)
        List<TAiread> aireads = aireadService.findAllTaireadByStationCodeAndTime(stationCode, firstDayOfYearAsDate, endDate);

        TAiread startAiread = null;
        TAiread endAiread = null;

        for (TAiread airead : aireads) {
            //过滤无效记录
            if (airead.getResultValue() == null || airead.getReviewResultFlag() == null ||
                    airead.getReviewResultFlag().equals(ReviewResultType.UNRECOGNIZABLE.getCode())) {
                continue;
            }
            if (startAiread == null) {
                startAiread = airead;
            }
            endAiread = airead;
        }

        long betweenDays = DateUtil.daysBetween(startAiread.getSampleTime(), endAiread.getSampleTime());
        if (betweenDays > 0) {
            return (endAiread.getResultValue().subtract(startAiread.getResultValue())).divide(new BigDecimal(betweenDays), 2, RoundingMode.HALF_UP);
        }
        return null;
    }

    //处理漏损任务
    private void handlePipelineLeakageTask(TPipelineLeakageModel model, int type) {
        //查站点对应的设备SN
        TAireadModel aireadModel = aireadService.findLatesTAireadByStationCode(model.getStationCode());
        if (aireadModel == null || StringUtils.isEmpty(aireadModel.getDeviceSn())) {
            return;
        }

        int interval = type == 1 ? model.getMeasurementInterval() : BaseConstant.DEFAULT_SAMPLE_INTERVAL;
        byte taskStatus = type == 1 ? (byte) 1 : (byte) 2;

        //按测量间隔修改设备配置
        TaskRemoteCfgModel cfgModel = new TaskRemoteCfgModel();
        cfgModel.setDeviceSn(aireadModel.getDeviceSn());
        cfgModel.setSampleInterval(interval);
        cfgModel.setCreateMan("漏损检测");

        cfgModel = taskRemoteCfgService.saveTaskRemoteCfg(cfgModel);
        if (cfgModel != null) {
            if (type == 1) {
                model.setPreTaskId(cfgModel.getId());
            } else {
                model.setPostTaskId(cfgModel.getId());
            }

            model.setTaskStatus(taskStatus);
            //创建结束任务时,计算漏损
            if (type == 2) {
                calcPipelineLeakage(model);
            }
            updateTPipelineLeakage(model);
        }
    }

    /**
     * 检查数据合法性
     * @param model
     * @return
     */
    public String checkDataLegitimacy(TPipelineLeakageModel model) {
        if (model.getMeasurementInterval() < 30 || model.getMeasurementInterval() > BaseConstant.DEFAULT_SAMPLE_INTERVAL) {
            return "测量间隔必须在30-1440之间";
        }
        Date tomorrow = DateUtil.getZeroTimeOfDay(DateUtil.getOtherDateByCurrentDate(new Date(),1));
        if (model.getMeasurementDate().before( tomorrow)) {
            return "测量时间不能早于明天";
        }
        TPipelineLeakageExample example = new TPipelineLeakageExample();
        example.createCriteria()
                .andMeasurementDateEqualTo(model.getMeasurementDate())
                .andStationCodeEqualTo(model.getStationCode())
                .andTaskStatusLessThan((byte)2);
        if (selectByExample( example).size()>0){
            return "该站点该时间段有漏损检测任务未完成";
        }
        return null;
    }
}