package com.njcn.prepare;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.enums.common.LogEnum;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.utils.HttpResultUtil;
import com.njcn.device.biz.commApi.CommTerminalGeneralClient;
import com.njcn.device.biz.pojo.dto.*;
import com.njcn.device.biz.pojo.param.DeptGetLineParam;
import com.njcn.prepare.harmonic.enums.PrepareResponseEnum;
import com.njcn.prepare.harmonic.pojo.bo.BaseParam;
import com.njcn.prepare.harmonic.pojo.bo.CalculatedParam;
import com.njcn.user.api.DeptFeignClient;
import com.njcn.user.pojo.po.Dept;
import com.njcn.web.controller.BaseController;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.flow.entity.CmpStep;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author hongawen
 * @version 1.0.0
 * @date 2023年11月01日 10:20
 */
@Slf4j
@Api(tags = "算法执行中心")
@RestController
@RequestMapping("/executor")
@RequiredArgsConstructor
public class ExecutionCenter extends BaseController {

    private final CommTerminalGeneralClient commTerminalGeneralClient;
    private final DeptFeignClient deptFeignClient;

    @Resource
    private FlowExecutor flowExecutor;


    /***
     * 1、校验非全链执行时，tagNames节点标签集合必须为非空，否则提示---无可执行节点
     * 2、补招标识为true时，起始日期&截止日期不可为空
     * 3、算法需要的索引集合
     * @author hongawen
     * @date 2023/11/3 11:36
     * @param baseParam 执行的基础参数
     */
    private CalculatedParam judgeExecuteParam(BaseParam baseParam) {
        CalculatedParam calculatedParam = new CalculatedParam();

        if (!baseParam.isFullChain() && CollectionUtil.isEmpty(baseParam.getTagNames())) {
            throw new BusinessException(PrepareResponseEnum.NO_EXECUTOR_NODE);
        }

        if (baseParam.isRepair() && StrUtil.isAllEmpty(baseParam.getBeginTime(), baseParam.getEndTime())) {
            throw new BusinessException(PrepareResponseEnum.NO_REPAIR_DATE);
        }

        BeanUtil.copyProperties(baseParam, calculatedParam);
        return calculatedParam;
    }

    /***
     *
     * @author hongawen
     * @date 2023/11/7 14:44
     */
    private void dealResponse(CalculatedParam calculatedParam, LiteflowResponse liteflowResponse, String methodDescribe) {
        if (liteflowResponse.isSuccess()) {
            log.info("日期{}，{}执行{}成功", calculatedParam.getDataDate(), methodDescribe, calculatedParam.isFullChain() ? "全链" : "指定节点：".concat(String.join(StrPool.COMMA, calculatedParam.getTagNames())));
        } else {
            Map<String, List<CmpStep>> executeSteps = liteflowResponse.getExecuteSteps();
            CmpStep failStep = null;
            for (String key : executeSteps.keySet()) {
                List<CmpStep> cmpSteps = executeSteps.get(key);
                cmpSteps = cmpSteps.stream().filter(cmpStep -> !cmpStep.isSuccess()).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(cmpSteps)) {
                    failStep = cmpSteps.get(0);
                }
            }
            log.error("日期{}，{}执行{}失败，在执行{}失败，失败原因:{}"
                    , calculatedParam.getDataDate()
                    , methodDescribe
                    , calculatedParam.isFullChain() ? "全链" : "指定节点：".concat(String.join(StrPool.COMMA, calculatedParam.getTagNames()))
                    , failStep.getNodeId().concat(Objects.isNull(failStep.getTag()) ? "" : StrPool.DASHED.concat(failStep.getTag()))
                    , Objects.isNull(failStep.getException()) ? null : failStep.getException().getMessage());
        }
    }


    @OperateInfo(info = LogEnum.BUSINESS_COMMON)
    @ApiOperation("监测点算法执行链")
    @PostMapping("/measurementPointExecutor")
    @Async("asyncExecutor")
    public void measurementPointExecutor(@RequestBody BaseParam baseParam) {
        String methodDescribe = getMethodDescribe("measurementPointExecutor");
        //手动判断参数是否合法,
        CalculatedParam calculatedParam = judgeExecuteParam(baseParam);
        // 测点索引
        if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
            calculatedParam.setIdList(commTerminalGeneralClient.getRunMonitorIds().getData());
        }
        LiteflowResponse liteflowResponse;
        if (baseParam.isRepair()) {
            //补招时，起始日期、截止日期必填
            DateTime startDate = DateUtil.parse(baseParam.getBeginTime(), DatePattern.NORM_DATE_FORMAT);
            DateTime endDate = DateUtil.parse(baseParam.getEndTime(), DatePattern.NORM_DATE_FORMAT);
            long betweenDay = DateUtil.betweenDay(startDate, endDate, true);
            //递增日期执行算法链
            for (int i = 0; i < betweenDay; i++) {
                if (i != 0) {
                    startDate = DateUtil.offsetDay(startDate, 1);
                }
                calculatedParam.setDataDate(DateUtil.format(startDate, DatePattern.NORM_DATE_PATTERN));
                liteflowResponse = flowExecutor.execute2Resp("measurement_point", calculatedParam);
                dealResponse(calculatedParam, liteflowResponse, methodDescribe);
            }
        } else {
            //非补招
            liteflowResponse = flowExecutor.execute2Resp("measurement_point", calculatedParam);
            dealResponse(calculatedParam, liteflowResponse, methodDescribe);
        }
    }
    @OperateInfo(info = LogEnum.BUSINESS_COMMON)
    @ApiOperation("监测点算法执行链(按小时执行的算法)")
    @PostMapping("/measurementPointExecutorByHour")
    @Async("asyncExecutor")
    public HttpResult<Boolean> measurementPointExecutorByHour(@RequestBody BaseParam baseParam) {
        String methodDescribe = getMethodDescribe("measurementPointExecutorByHour");
        //手动判断参数是否合法,
        CalculatedParam calculatedParam = judgeExecuteParam(baseParam);
        // 测点索引
        if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
            calculatedParam.setIdList(commTerminalGeneralClient.getRunMonitorIds().getData());
        }
        LiteflowResponse liteflowResponse;
        if (baseParam.isRepair()) {
            //补招时，起始日期、截止日期必填
            DateTime startDate = DateUtil.parse(baseParam.getBeginTime(), DatePattern.NORM_DATETIME_FORMAT);
            DateTime endDate = DateUtil.parse(baseParam.getEndTime(), DatePattern.NORM_DATETIME_FORMAT);
            long betweenHour = DateUtil.between(startDate, endDate, DateUnit.HOUR);
            //递增日期执行算法链
            for (int i = 0; i < betweenHour; i++) {
                if (i != 0) {
                    startDate = DateUtil.offsetHour(startDate, 1);
                }
                calculatedParam.setDataDate(DateUtil.format(startDate, DatePattern.NORM_DATETIME_FORMATTER));
                liteflowResponse = flowExecutor.execute2Resp("measurement_point_hour", calculatedParam);
                dealResponse(calculatedParam, liteflowResponse, methodDescribe);
            }
        } else {
            //非补招
            calculatedParam.setDataDate(DateUtil.format(DateUtil.offsetHour( DateUtil.parse(baseParam.getDataDate(), DatePattern.NORM_DATETIME_FORMAT),-1), DatePattern.NORM_DATETIME_FORMATTER));
            liteflowResponse = flowExecutor.execute2Resp("measurement_point_hour", calculatedParam);
            dealResponse(calculatedParam, liteflowResponse, methodDescribe);
        }
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe);
    }

    @OperateInfo(info = LogEnum.BUSINESS_COMMON)
    @ApiOperation("装置算法执行链")
    @PostMapping("/deviceExecutor")
    @Async("asyncExecutor")
    public void deviceExecutor(@RequestBody BaseParam baseParam) {
        String methodDescribe = getMethodDescribe("deviceExecutor");
        //手动判断参数是否合法,
        CalculatedParam calculatedParam = judgeExecuteParam(baseParam);
        DeptGetLineParam deptGetLineParam = new DeptGetLineParam();
        // 设备索引
        if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
            Dept data = deptFeignClient.getRootDept().getData();
            deptGetLineParam.setDeptId(data.getId());
            List<DeptGetDeviceDTO> list = commTerminalGeneralClient.deptGetDevice(deptGetLineParam).getData();
            DeptGetDeviceDTO dto = list.stream().filter(po -> Objects.equals(po.getUnitId(), data.getId())).collect(Collectors.toList()).get(0);
            List<LineDevGetDTO> devList = dto.getDeviceList();
            calculatedParam.setIdList(devList.stream().map(LineDevGetDTO::getDevId).distinct().collect(Collectors.toList()));
        }
        LiteflowResponse liteflowResponse;
        if (baseParam.isRepair()) {
            //补招时，起始日期、截止日期必填
            DateTime startDate = DateUtil.parse(baseParam.getBeginTime(), DatePattern.NORM_DATE_FORMAT);
            DateTime endDate = DateUtil.parse(baseParam.getEndTime(), DatePattern.NORM_DATE_FORMAT);
            long betweenDay = DateUtil.betweenDay(startDate, endDate, true);
            //递增日期执行算法链
            for (int i = 0; i < betweenDay; i++) {
                if (i != 0) {
                    startDate = DateUtil.offsetDay(startDate, 1);
                }
                calculatedParam.setDataDate(DateUtil.format(startDate, DatePattern.NORM_DATE_PATTERN));
                liteflowResponse = flowExecutor.execute2Resp("device", calculatedParam);
                dealResponse(calculatedParam, liteflowResponse, methodDescribe);
            }
        } else {
            //非补招
            liteflowResponse = flowExecutor.execute2Resp("device", calculatedParam);
            dealResponse(calculatedParam, liteflowResponse, methodDescribe);
        }
    }

    @OperateInfo(info = LogEnum.BUSINESS_COMMON)
    @ApiOperation("单位监测点算法执行链")
    @PostMapping("/orgPointExecutor")
    @Async("asyncExecutor")
    public void orgPointExecutor(@RequestBody BaseParam baseParam) {
        String methodDescribe = getMethodDescribe("orgPointExecutor");
        //手动判断参数是否合法,
        CalculatedParam<DeptGetChildrenMoreDTO> calculatedParam = judgeExecuteParam(baseParam);
        // 测点索引
        DeptGetLineParam deptGetLineParam = new DeptGetLineParam();

        if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
            Dept data = deptFeignClient.getRootDept().getData();
            deptGetLineParam.setDeptId(data.getId());
            calculatedParam.setIdList(commTerminalGeneralClient.deptGetLine(deptGetLineParam).getData());
        }
        LiteflowResponse liteflowResponse;
        if (baseParam.isRepair()) {
            //补招时，起始日期、截止日期必填
            DateTime startDate = DateUtil.parse(baseParam.getBeginTime(), DatePattern.NORM_DATE_FORMAT);
            DateTime endDate = DateUtil.parse(baseParam.getEndTime(), DatePattern.NORM_DATE_FORMAT);
            long betweenDay = DateUtil.betweenDay(startDate, endDate, true);
            //递增日期执行算法链
            for (int i = 0; i < betweenDay; i++) {
                if (i != 0) {
                    startDate = DateUtil.offsetDay(startDate, 1);
                }
                calculatedParam.setDataDate(DateUtil.format(startDate, DatePattern.NORM_DATE_PATTERN));
                liteflowResponse = flowExecutor.execute2Resp("org_point", calculatedParam);
                dealResponse(calculatedParam, liteflowResponse, methodDescribe);
            }
        } else {
            //非补招
            liteflowResponse = flowExecutor.execute2Resp("org_point", calculatedParam);
            dealResponse(calculatedParam, liteflowResponse, methodDescribe);
        }

    }

    @OperateInfo(info = LogEnum.BUSINESS_COMMON)
    @ApiOperation("pms国网上送单位层级算法执行链")
    @PostMapping("/uploadOrgExecutor")
    @Async("asyncExecutor")
    public void uploadOrgExecutor(@RequestBody BaseParam baseParam) {
        String methodDescribe = getMethodDescribe("uploadOrgExecutor");
        //手动判断参数是否合法,
        CalculatedParam<DeptGetChildrenMoreDTO> calculatedParam = judgeExecuteParam(baseParam);
        // 测点索引
        DeptGetLineParam deptGetLineParam = new DeptGetLineParam();

        if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
            Dept data = deptFeignClient.getRootDept().getData();
            deptGetLineParam.setDeptId(data.getId());
            calculatedParam.setIdList(commTerminalGeneralClient.deptGetLine(deptGetLineParam).getData());
        }
        LiteflowResponse liteflowResponse;
        if (baseParam.isRepair()) {
            //补招时，起始日期、截止日期必填
            DateTime startDate = DateUtil.parse(baseParam.getBeginTime(), DatePattern.NORM_DATE_FORMAT);
            DateTime endDate = DateUtil.parse(baseParam.getEndTime(), DatePattern.NORM_DATE_FORMAT);
            long betweenDay = DateUtil.betweenDay(startDate, endDate, true);
            //递增日期执行算法链
            for (int i = 0; i < betweenDay; i++) {
                if (i != 0) {
                    startDate = DateUtil.offsetDay(startDate, 1);
                }
                calculatedParam.setDataDate(DateUtil.format(startDate, DatePattern.NORM_DATE_PATTERN));
                liteflowResponse = flowExecutor.execute2Resp("upload_org", calculatedParam);
                dealResponse(calculatedParam, liteflowResponse, methodDescribe);
            }
        } else {
            //非补招
            liteflowResponse = flowExecutor.execute2Resp("upload_org", calculatedParam);
            dealResponse(calculatedParam, liteflowResponse, methodDescribe);
        }

    }

    @OperateInfo(info = LogEnum.BUSINESS_COMMON)
    @ApiOperation("变电站算法执行链")
    @PostMapping("/substationExecutor")
    @Async("asyncExecutor")
    public void substationExecutor(@RequestBody BaseParam baseParam) {
        String methodDescribe = getMethodDescribe("substationExecutor");
        //手动判断参数是否合法,
        CalculatedParam<String> calculatedParam = judgeExecuteParam(baseParam);
        // 测点索引
        DeptGetLineParam deptGetLineParam = new DeptGetLineParam();

        if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
            Dept data = deptFeignClient.getRootDept().getData();
            deptGetLineParam.setDeptId(data.getId());
            List<DeptGetSubStationDTO> data1 = commTerminalGeneralClient.deptSubStation(deptGetLineParam).getData();
            List<String> stationIds = new ArrayList<>();
            for (DeptGetSubStationDTO deptGetSubStationDTO : data1) {
                Collection<String> union = CollectionUtils.union(Optional.ofNullable(deptGetSubStationDTO.getStationIds()).orElse(new ArrayList<String>()),
                        Optional.ofNullable(deptGetSubStationDTO.getStationIds()).orElse(new ArrayList<String>()));
                List<String> collect = union.stream().distinct().collect(Collectors.toList());
                stationIds.addAll(collect);
            }
            stationIds = stationIds.stream().distinct().collect(Collectors.toList());
            calculatedParam.setIdList(stationIds);
        }
        LiteflowResponse liteflowResponse;
        if (baseParam.isRepair()) {
            //补招时，起始日期、截止日期必填
            DateTime startDate = DateUtil.parse(baseParam.getBeginTime(), DatePattern.NORM_DATE_FORMAT);
            DateTime endDate = DateUtil.parse(baseParam.getEndTime(), DatePattern.NORM_DATE_FORMAT);
            long betweenDay = DateUtil.betweenDay(startDate, endDate, true);
            //递增日期执行算法链
            for (int i = 0; i < betweenDay; i++) {
                if (i != 0) {
                    startDate = DateUtil.offsetDay(startDate, 1);
                }
                calculatedParam.setDataDate(DateUtil.format(startDate, DatePattern.NORM_DATE_PATTERN));
                liteflowResponse = flowExecutor.execute2Resp("sub_station", calculatedParam);
                dealResponse(calculatedParam, liteflowResponse, methodDescribe);
            }
        } else {
            //非补招
            liteflowResponse = flowExecutor.execute2Resp("sub_station", calculatedParam);
            dealResponse(calculatedParam, liteflowResponse, methodDescribe);
        }

    }

    /**
     * pms dim母线，电站运行情况
     *
     * @author cdf
     * @date 2023/11/17
     */
    @OperateInfo(info = LogEnum.BUSINESS_COMMON)
    @ApiOperation("pms变电站母线算法执行链")
    @PostMapping("/pmsdimexecutor")
    public void pmsDimExecutor(@RequestBody BaseParam baseParam) {
        String methodDescribe = getMethodDescribe("pmsdimexecutor");
        //手动判断参数是否合法,
        CalculatedParam<DeptGetChildrenDTO> calculatedParam = judgeExecuteParam(baseParam);
        // 测点索引
        DeptGetLineParam deptGetLineParam = new DeptGetLineParam();

        if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
            Dept data = deptFeignClient.getRootDept().getData();
            deptGetLineParam.setDeptId(data.getId());
            deptGetLineParam.setSystemType(0);
            List<DeptGetChildrenDTO> dept = commTerminalGeneralClient.deptGetLineList(deptGetLineParam).getData();
            calculatedParam.setIdList(dept);
        }
        LiteflowResponse liteflowResponse;
        if (baseParam.isRepair()) {
            //补招时，起始日期、截止日期必填
            DateTime startDate = DateUtil.parse(baseParam.getBeginTime(), DatePattern.NORM_DATE_FORMAT);
            DateTime endDate = DateUtil.parse(baseParam.getEndTime(), DatePattern.NORM_DATE_FORMAT);
            long betweenDay = DateUtil.betweenDay(startDate, endDate, true);
            //递增日期执行算法链
            for (int i = 0; i < betweenDay; i++) {
                if (i != 0) {
                    startDate = DateUtil.offsetDay(startDate, 1);
                }
                calculatedParam.setDataDate(DateUtil.format(startDate, DatePattern.NORM_DATE_PATTERN));
                liteflowResponse = flowExecutor.execute2Resp("dim_station_busbar", calculatedParam);
                dealResponse(calculatedParam, liteflowResponse, methodDescribe);
            }
        } else {
            //非补招
            liteflowResponse = flowExecutor.execute2Resp("dim_station_busbar", calculatedParam);
            dealResponse(calculatedParam, liteflowResponse, methodDescribe);
        }

    }

    @OperateInfo(info = LogEnum.BUSINESS_COMMON)
    @ApiOperation("母线算法执行链(主网测点)")
    @PostMapping("/generaTrixExecutor")
    @Async("asyncExecutor")
    public void generaTrixExecutor(@RequestBody BaseParam baseParam) {
        String methodDescribe = getMethodDescribe("generaTrixExecutor");
        //手动判断参数是否合法,
        CalculatedParam<DeptGetBusBarDTO> calculatedParam = judgeExecuteParam(baseParam);
        //母线索引
        if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
            Dept dept = deptFeignClient.getRootDept().getData();
            DeptGetLineParam deptGetLineParam = new DeptGetLineParam();
            deptGetLineParam.setDeptId(dept.getId());
            deptGetLineParam.setSystemType(0);
            List<DeptGetBusBarDTO> busBarList = commTerminalGeneralClient.deptBusBar(deptGetLineParam).getData();
            calculatedParam.setIdList(busBarList);
        }
        LiteflowResponse liteflowResponse;
        if (baseParam.isRepair()) {
            //补招时，起始日期、截止日期必填
            DateTime startDate = DateUtil.parse(baseParam.getBeginTime(), DatePattern.NORM_DATE_FORMAT);
            DateTime endDate = DateUtil.parse(baseParam.getEndTime(), DatePattern.NORM_DATE_FORMAT);
            long betweenDay = DateUtil.betweenDay(startDate, endDate, true);
            //递增日期执行算法链
            for (int i = 0; i < betweenDay; i++) {
                if (i != 0) {
                    startDate = DateUtil.offsetDay(startDate, 1);
                }
                calculatedParam.setDataDate(DateUtil.format(startDate, DatePattern.NORM_DATE_PATTERN));
                liteflowResponse = flowExecutor.execute2Resp("genera_trix", calculatedParam);
                dealResponse(calculatedParam, liteflowResponse, methodDescribe);
            }
        } else {
            //非补招
            liteflowResponse = flowExecutor.execute2Resp("genera_trix", calculatedParam);
            dealResponse(calculatedParam, liteflowResponse, methodDescribe);
        }
    }

    @OperateInfo(info = LogEnum.BUSINESS_COMMON)
    @ApiOperation("单位变电站算法执行链")
    @PostMapping("/orgSubStationExecutor")
    @Async("asyncExecutor")
    public void orgSubStationExecutor(@RequestBody BaseParam baseParam) {
        String methodDescribe = getMethodDescribe("orgSubStationExecutor");
        //手动判断参数是否合法,
        CalculatedParam<DeptGetSubStationDTO.Info> calculatedParam = judgeExecuteParam(baseParam);
        // 测点索引
        DeptGetLineParam deptGetLineParam = new DeptGetLineParam();
        if (CollectionUtils.isEmpty(calculatedParam.getIdList())) {
            Dept data = deptFeignClient.getRootDept().getData();
            deptGetLineParam.setDeptId(data.getId());
            List<DeptGetSubStationDTO.Info> data1 = commTerminalGeneralClient.deptGetSubStationInfo(deptGetLineParam).getData();
            calculatedParam.setIdList(data1);
        }
        LiteflowResponse liteflowResponse;
        if (baseParam.isRepair()) {
            //补招时，起始日期、截止日期必填
            DateTime startDate = DateUtil.parse(baseParam.getBeginTime(), DatePattern.NORM_DATE_FORMAT);
            DateTime endDate = DateUtil.parse(baseParam.getEndTime(), DatePattern.NORM_DATE_FORMAT);
            long betweenDay = DateUtil.betweenDay(startDate, endDate, true);
            //递增日期执行算法链
            for (int i = 0; i < betweenDay; i++) {
                if (i != 0) {
                    startDate = DateUtil.offsetDay(startDate, 1);
                }
                calculatedParam.setDataDate(DateUtil.format(startDate, DatePattern.NORM_DATE_PATTERN));
                liteflowResponse = flowExecutor.execute2Resp("orgSub_station", calculatedParam);
                dealResponse(calculatedParam, liteflowResponse, methodDescribe);
            }
        } else {
            //非补招
            liteflowResponse = flowExecutor.execute2Resp("orgSub_station", calculatedParam);
            dealResponse(calculatedParam, liteflowResponse, methodDescribe);
        }
    }

}
