package com.njcn.supervision.service.device.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import com.njcn.bpm.api.BpmProcessFeignClient;
import com.njcn.bpm.enums.BpmProcessInstanceStatusEnum;
import com.njcn.bpm.enums.BpmTaskStatusEnum;
import com.njcn.bpm.pojo.dto.BpmInstanceInfo;
import com.njcn.bpm.pojo.dto.BpmProcessInstanceCreateReqDTO;
import com.njcn.bpm.pojo.param.instance.BpmProcessInstanceCancelParam;
import com.njcn.bpm.pojo.vo.TodoVO;
import com.njcn.db.constant.DbConstant;
import com.njcn.device.biz.commApi.CommTerminalGeneralClient;
import com.njcn.device.biz.pojo.dto.LineDevGetDTO;
import com.njcn.device.biz.pojo.param.DeptGetLineParam;
import com.njcn.device.pq.api.DeviceFeignClient;
import com.njcn.device.pq.api.GeneralDeviceInfoClient;
import com.njcn.device.pq.pojo.vo.DevDetail;
import com.njcn.supervision.enums.SupervisionKeyEnum;
import com.njcn.supervision.mapper.device.CheckDeviceMapper;
import com.njcn.supervision.pojo.param.device.CheckDeviceParam;
import com.njcn.supervision.pojo.po.device.CheckDevice;
import com.njcn.supervision.pojo.vo.device.CheckDeviceVo;
import com.njcn.supervision.service.device.ICheckDeviceService;
import com.njcn.supervision.utils.InstanceUtil;
import com.njcn.user.api.DeptFeignClient;
import com.njcn.user.api.UserFeignClient;
import com.njcn.user.pojo.po.Dept;
import com.njcn.user.pojo.vo.UserVO;
import com.njcn.web.factory.PageFactory;
import com.njcn.web.utils.RequestUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 终端周期检测表 服务实现类
 * </p>
 *
 * @author xy
 * @since 2024-06-13
 */
@Service
@RequiredArgsConstructor
public class CheckDeviceServiceImpl extends MppServiceImpl<CheckDeviceMapper, CheckDevice> implements ICheckDeviceService {

    private final CommTerminalGeneralClient commTerminalGeneralClient;

    private final GeneralDeviceInfoClient generalDeviceInfoClient;

    private final BpmProcessFeignClient bpmProcessFeignClient;

    private final DeptFeignClient deptFeignClient;

    private final DeviceFeignClient deviceFeignClient;

    private final UserFeignClient userFeignClient;

    @Override
    public void addCheckDevice() {
        DeptGetLineParam deptGetLineParam = new DeptGetLineParam();
        Dept data = deptFeignClient.getRootDept().getData();
        deptGetLineParam.setDeptId(data.getId());
        List<String> devList = commTerminalGeneralClient.deptGetDevice(deptGetLineParam)
                .getData()
                .stream()
                .flatMap(item -> item.getDeviceList().stream())
                .map(LineDevGetDTO::getDevId)
                .distinct()
                .collect(Collectors.toList());
        List<DevDetail> detail = generalDeviceInfoClient.getDevInfoByIds(devList).getData();
        List<CheckDevice> checkDevices = detail.stream()
                .filter(item -> item.getNextTimeCheck().isBefore(LocalDate.now()) || LocalDate.now().isEqual(item.getNextTimeCheck()))
                .map(item -> {
                    CheckDevice checkDevice = new CheckDevice();
                    checkDevice.setDeviceId(item.getDevIndex());
                    checkDevice.setCheckTime(item.getNextTimeCheck());
                    checkDevice.setOverdueDay(new Long(item.getNextTimeCheck().until(LocalDate.now(), ChronoUnit.DAYS)).intValue());
                    checkDevice.setState(0);
                    return checkDevice;
                })
                .collect(Collectors.toList());
        this.saveOrUpdateBatchByMultiId(checkDevices, 100);
    }

    @Override
    public Page<CheckDeviceVo> getCheckDeviceList(CheckDeviceParam param) {
        QueryWrapper<CheckDeviceVo> queryWrapper = new QueryWrapper<>();
        DeptGetLineParam deptGetLineParam = new DeptGetLineParam();
        deptGetLineParam.setDeptId(param.getDeptId());
        List<String> devList = commTerminalGeneralClient.deptGetDevice(deptGetLineParam)
                .getData()
                .stream()
                .flatMap(item -> item.getDeviceList().stream())
                .map(LineDevGetDTO::getDevId)
                .distinct()
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(devList)) {
            queryWrapper.in("A.device_id", devList);
        }
        if (ObjectUtil.isNotNull(param)) {
            //查询参数不为空，进行条件填充
            if (StrUtil.isNotBlank(param.getSearchValue())) {
                queryWrapper
                        .and(par -> par.like("B.Name", param.getSearchValue()).or().like("B1.Name", param.getSearchValue()));
            }
            //排序
            if (ObjectUtil.isAllNotEmpty(param.getSortBy(), param.getOrderBy())) {
                queryWrapper.orderBy(true, param.getOrderBy().equalsIgnoreCase(DbConstant.ASC), StrUtil.toUnderlineCase(param.getSortBy()));
            } else {
                //默认根据逾期天数排序
                queryWrapper.orderBy(true, false, "A.overdue_day").orderBy(true, false, "A.status");
            }
            queryWrapper.and(wrapper ->
                    wrapper.isNull("A.status")
                            .or()
                            .eq(ObjUtil.isNotNull(param.getStatus()), "A.status", param.getStatus())
            );
            if (!Objects.isNull(param.getState())) {
                queryWrapper.eq("A.state", param.getState());
            }
        }
        queryWrapper.le("A.check_time", LocalDate.now());
        return this.baseMapper.page(new Page<>(PageFactory.getPageNum(param), PageFactory.getPageSize(param)), queryWrapper);
    }

    @Override
    public CheckDeviceVo getInfoById(String id) {
        return this.baseMapper.getInfo(id);
    }

    @Override
    public void submitReport(CheckDeviceParam param) {
        CheckDevice checkDevice = this.baseMapper.selectById(param.getId());
        checkDevice.setCheckFilePath(param.getReportPath());
        checkDevice.setStatus(BpmTaskStatusEnum.RUNNING.getStatus());
        checkDevice.setNowCheckTime(LocalDate.parse(param.getNowCheckTime(), DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));
        // 发起 BPM 流程
        Map<String, Object> processInstanceVariables = new HashMap<>(16);
        BpmProcessInstanceCreateReqDTO bpmProcessInstanceCreateReqDTO = new BpmProcessInstanceCreateReqDTO();
        bpmProcessInstanceCreateReqDTO.setProcessDefinitionKey(SupervisionKeyEnum.TIMING_CHECK_DEVICE.getKey());
        bpmProcessInstanceCreateReqDTO.setBusinessKey(checkDevice.getId());
        bpmProcessInstanceCreateReqDTO.setStartUserSelectAssignees(new HashMap<>(16));
        bpmProcessInstanceCreateReqDTO.setVariables(processInstanceVariables);
        String processInstanceId = bpmProcessFeignClient.createProcessInstance(RequestUtil.getUserIndex(), bpmProcessInstanceCreateReqDTO).getData();
        // 将工作流的编号，更新到流程单中
        checkDevice.setState(1);
        checkDevice.setProcessInstanceId(processInstanceId);
        checkDevice.setDescription(param.getDescription());
        checkDevice.setCreateBy(RequestUtil.getUserIndex());
        this.baseMapper.updateById(checkDevice);
    }

    @Override
    public void reloadReport(CheckDeviceParam param) {
        CheckDevice checkDevice = this.baseMapper.selectById(param.getId());
        //判断是否有权限操作
        InstanceUtil.judgeUserPower(RequestUtil.getUserIndex(), checkDevice.getCreateBy());
        //处理历史流程id列表
        String historyInstanceIds = InstanceUtil.dealHistoryId(checkDevice.getProcessInstanceId(), checkDevice.getHistoryInstanceId());
        checkDevice.setStatus(BpmTaskStatusEnum.RUNNING.getStatus());
        checkDevice.setCheckFilePath(param.getReportPath());
        checkDevice.setDescription(param.getDescription());
        checkDevice.setNowCheckTime(LocalDate.parse(param.getNowCheckTime(), DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));
        // 发起 BPM 流程
        Map<String, Object> processInstanceVariables = new HashMap<>(16);
        BpmProcessInstanceCreateReqDTO bpmProcessInstanceCreateReqDTO = new BpmProcessInstanceCreateReqDTO();
        bpmProcessInstanceCreateReqDTO.setProcessDefinitionKey(SupervisionKeyEnum.TIMING_CHECK_DEVICE.getKey());
        bpmProcessInstanceCreateReqDTO.setBusinessKey(checkDevice.getId());
        bpmProcessInstanceCreateReqDTO.setStartUserSelectAssignees(new HashMap<>(16));
        bpmProcessInstanceCreateReqDTO.setVariables(processInstanceVariables);
        String processInstanceId = bpmProcessFeignClient.createProcessInstance(checkDevice.getCreateBy(), bpmProcessInstanceCreateReqDTO).getData();
        // 将工作流的编号，更新到流程单中
        checkDevice.setProcessInstanceId(processInstanceId);
        //保存历史流程id列表
        checkDevice.setHistoryInstanceId(historyInstanceIds);
        this.baseMapper.updateById(checkDevice);
    }

    @Override
    public String cancel(BpmProcessInstanceCancelParam cancelReqVO) {
        CheckDevice checkDevice = this.baseMapper.selectById(cancelReqVO.getId());
        //判断是否有权限操作
        InstanceUtil.judgeUserPower(RequestUtil.getUserIndex(), checkDevice.getCreateBy());
        //取消流程
        bpmProcessFeignClient.cancelProcessInstanceByStartUser(cancelReqVO);
        checkDevice.setStatus(BpmProcessInstanceStatusEnum.CANCEL.getStatus());
        this.updateById(checkDevice);
        return checkDevice.getId();
    }

    @Override
    public List<TodoVO> getTodoBusiness() {
        List<TodoVO> todoVOList = new ArrayList<>();
        //如果用户为管理员则不查
        UserVO userVO = userFeignClient.getUserById(RequestUtil.getUserIndex()).getData();
        if(Objects.nonNull(userVO) && userVO.getType() == 1){
            return todoVOList;
        }
        QueryWrapper<CheckDeviceVo> queryWrapper = new QueryWrapper<>();
        DeptGetLineParam deptGetLineParam = new DeptGetLineParam();
        deptGetLineParam.setDeptId(RequestUtil.getDeptIndex());
        List<String> devList = commTerminalGeneralClient.deptGetDevice(deptGetLineParam)
                .getData()
                .stream()
                .flatMap(item -> item.getDeviceList().stream())
                .map(LineDevGetDTO::getDevId)
                .distinct()
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(devList)) {
            queryWrapper.in("A.device_id", devList);
        }else{
            return new ArrayList<>();
        }
        //查询出今天之前要待检的终端
        queryWrapper.le("A.check_time", LocalDate.now())
                .eq("A.state", 0);
        List<CheckDeviceVo> checkDeviceVoList = this.baseMapper.selectVOList(queryWrapper);
        if (CollUtil.isNotEmpty(checkDeviceVoList)) {
            todoVOList = checkDeviceVoList.stream().map(item -> {
                TodoVO todoVO = new TodoVO();
                todoVO.setId(item.getId());
                todoVO.setTaskName("终端周期定检");
                todoVO.setTaskContent("变电站:".concat(item.getSubstation())
                        .concat(",装置:").concat(item.getDeviceName())
                        .concat(",本次定检时间为:").concat(LocalDateTimeUtil.format(item.getThisTimeCheck(),DatePattern.NORM_DATE_PATTERN))
                        .concat(",逾期:").concat(String.valueOf(item.getOverdueDay()))
                        .concat("天")
                );
                todoVO.setStartUser("系统扫描");
                todoVO.setTaskCreateTime(item.getCreateTime());
                todoVO.setSource(3);
                todoVO.setRoutePath("terminalNetwotk");
                todoVO.setTabValue(4);
                return todoVO;
            }).collect(Collectors.toList());
        }
        return todoVOList;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProcessStatus(String businessId, Integer status) {
        //流程状态整改
        CheckDevice checkDevice = this.baseMapper.selectById(businessId);
        checkDevice.setStatus(status);
        checkDevice.setState(2);
        this.updateById(checkDevice);
        //修改pq_device定检时间和下次定检时间 5年
        String nowCheckTime = checkDevice.getNowCheckTime().format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN));
        String nextCheckTime = checkDevice.getNowCheckTime().plusYears(5).format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN));
        deviceFeignClient.updateDevCheckTime(checkDevice.getDeviceId(), nowCheckTime, nextCheckTime);
    }

    @Override
    public BpmInstanceInfo getInstanceInfo(String businessId) {
        BpmInstanceInfo bpmInstanceInfo = null;
        CheckDeviceVo checkDevice = this.getInfoById(businessId);
        if (ObjUtil.isNotNull(checkDevice)) {
            bpmInstanceInfo = new BpmInstanceInfo();
            bpmInstanceInfo.setHistoryInstanceId(checkDevice.getHistoryInstanceId());
            String sign = checkDevice.getDept()
                    .concat(StrPool.SLASH)
                    .concat(checkDevice.getSubstation())
                    .concat(StrPool.SLASH)
                    .concat(checkDevice.getDeviceName());
            bpmInstanceInfo.setInstanceSign(sign);
        }
        return bpmInstanceInfo;
    }

    @Override
    public Set<String> getAllRejectInstanceIds() {
        Set<String> rejectInstanceIds = new HashSet<>();
        // 查询所有走了流程，但是流程状态为不通过的流程实例id
        LambdaQueryWrapper<CheckDevice> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(CheckDevice::getCreateBy, RequestUtil.getUserIndex())
                .eq(CheckDevice::getStatus, BpmProcessInstanceStatusEnum.REJECT.getStatus())
                .select(CheckDevice::getProcessInstanceId);
        List<CheckDevice> checkDevices = this.baseMapper.selectList(lambdaQueryWrapper);
        List<String> result = checkDevices.stream().map(CheckDevice::getProcessInstanceId).collect(Collectors.toList());
        if(CollectionUtil.isNotEmpty(result)){
            rejectInstanceIds.addAll(result);
        }
        return rejectInstanceIds;
    }
}
