package com.tykj.dev.device.repair.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.tykj.dev.blockcha.subject.entity.BcHash;
import com.tykj.dev.blockcha.subject.service.BlockChainUtil;
import com.tykj.dev.config.TaskBeanConfig;
import com.tykj.dev.device.library.service.DeviceLibraryService;
import com.tykj.dev.device.library.service.DeviceLogService;
import com.tykj.dev.device.library.subject.Dto.DeviceLogDto;
import com.tykj.dev.device.library.subject.domin.DeviceLibrary;
import com.tykj.dev.device.library.subject.vo.Script;
import com.tykj.dev.device.library.subject.vo.ScriptSaveVo;
import com.tykj.dev.device.repair.repository.RepairBillDao;
import com.tykj.dev.device.repair.repository.RepairDetailDao;
import com.tykj.dev.device.repair.repository.RepairSendBillDao;
import com.tykj.dev.device.repair.service.RepairDetailService;
import com.tykj.dev.device.repair.subject.domin.RepairBill;
import com.tykj.dev.device.repair.subject.domin.RepairDetail;
import com.tykj.dev.device.repair.subject.domin.RepairSendBill;
import com.tykj.dev.device.repair.subject.vo.BillVo;
import com.tykj.dev.device.repair.subject.vo.ClearRepairVo;
import com.tykj.dev.device.repair.subject.vo.ClearTaskVo;
import com.tykj.dev.device.repair.subject.vo.SetDevicesOwnUnit;
import com.tykj.dev.device.task.service.TaskService;
import com.tykj.dev.device.task.subject.bto.TaskBto;
import com.tykj.dev.device.task.subject.domin.Task;
import com.tykj.dev.device.user.read.subject.bto.MessageBto;
import com.tykj.dev.device.user.subject.entity.Units;
import com.tykj.dev.device.user.subject.entity.User;
import com.tykj.dev.device.user.subject.service.UnitsService;
import com.tykj.dev.device.user.util.UserUtils;
import com.tykj.dev.misc.base.BusinessEnum;
import com.tykj.dev.misc.base.RepairStatusEnum;
import com.tykj.dev.misc.base.StatusEnum;
import com.tykj.dev.misc.exception.ApiException;
import com.tykj.dev.misc.utils.JacksonUtil;
import com.tykj.dev.misc.utils.ResultUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author zjm
 * @version 1.0.0
 * @ClassName DeviceRepairDetailServiceImpl.java
 * @Description TODO
 * @createTime 2020年08月25日 10:37:00
 */
@Service
public class RepairDetailServiceImpl implements RepairDetailService {
    @Autowired
    RepairDetailDao deviceRepairDetailDao;

    @Autowired
    private BlockChainUtil blockChainUtil;

    @Autowired
    private TaskService taskService;

    @Autowired
    private DeviceLibraryService deviceLibraryService;

    @Autowired
    RepairSendBillDao repairSendBillDao;

    @Autowired
    private RepairBillDao repairBillDao;

    @Autowired
    private UnitsService unitsService;

    @Autowired
    private UserUtils userUtils;

    @Autowired
    private DeviceLogService deviceLogService;

    @Override
    public RepairDetail save(RepairDetail deviceRepairDetailEntity) {
        RepairDetail repairDetail = deviceRepairDetailDao.save(deviceRepairDetailEntity);
        CompletableFuture.runAsync(()->{
            RepairDetail repairDetail1 = getOne(repairDetail.getId());
            BcHash bcText = blockChainUtil.sendHash(1000, JacksonUtil.toJSon(repairDetail1));
            String recordId = bcText.getData().getRecordID();
            repairDetail1.setRecordId(recordId);
            deviceRepairDetailDao.save(repairDetail1);
        }, TaskBeanConfig.getThreadPoolTaskScheduler());
        return repairDetail;
    }

    @Override
    public boolean delete(Integer id) {
        return false;
    }

    @Override
    public List<RepairDetail> findAll() {
        return deviceRepairDetailDao.findAll();
    }

    @Override
    public RepairDetail update(RepairDetail deviceRepairDetailEntity) {
        RepairDetail repairDetail = deviceRepairDetailDao.save(deviceRepairDetailEntity);
        CompletableFuture.runAsync(()-> blockChainUtil.appendHash(JacksonUtil.toJSon(repairDetail),repairDetail.getRecordId()), TaskBeanConfig.getThreadPoolTaskScheduler());
        return repairDetail;
    }

    @Override
    public List<RepairDetail> findByBillId(Integer billId) {
        List<RepairDetail> repairDetails = deviceRepairDetailDao.findByDeviceRepairBillId(billId);
        repairDetails.forEach(RepairDetail::setConfigName);
        return repairDetails;
    }

    @Override
    public List<RepairDetail> findByBackBillId(Integer billId) {
        List<RepairDetail> repairDetails = deviceRepairDetailDao.findByRepairBackBillId(billId);
        repairDetails.forEach(RepairDetail::setConfigName);
        return repairDetails;
    }

    @Override
    public RepairDetail updateIdAndStatus(Integer id, Integer status) {
        Optional<RepairDetail> repairDetail = deviceRepairDetailDao.findById(id);
        if (repairDetail.isPresent()) {
            RepairDetail repairDetail1 = repairDetail.get();
            repairDetail1.setRepairStatus(status);
            deviceRepairDetailDao.save(repairDetail1);
            return repairDetail1;
        } else {
            throw new ApiException(ResultUtil.failed("所查询ID不存在"));
        }
    }

    /**
     * @param id 主键id
     *           根据主键Id查询
     */
    @Override
    public RepairDetail getOne(Integer id) {
        Optional<RepairDetail> repairDetail = deviceRepairDetailDao.findById(id);
        if (repairDetail.isPresent()) {
            return repairDetail.get().setConfigName();
        } else {
            throw new ApiException(ResultUtil.failed("所查询ID不存在"));
        }
    }

    /**
     * @param repairDetails 异步上链
     */
    @Override
    @Async
    public void sendHash(List<RepairDetail> repairDetails) {
        repairDetails.forEach(repairDetail -> {
            BcHash bcText = blockChainUtil.sendHash(1000, JacksonUtil.toJSon(repairDetail));
            String recordId = bcText.getData().getRecordID();
            repairDetail.setRecordId(recordId);
            update(repairDetail);
        });
    }

    @Override
    public Map<Integer, ClearTaskVo> getClearTaskVo(List<Integer> deviceIds) {
        List<ClearTaskVo> clearTaskVos = new ArrayList<>();
        Map<Integer,ClearTaskVo> map1 = new HashMap<>();
        //通过装备id查询出账单id (送修单repairBill)
        List<RepairDetail> repairDetails = deviceRepairDetailDao.findAllByDeviceIdIn(deviceIds);
        //取出装备id相同的最新时间
        Map<Integer,RepairDetail> map =new HashMap<>();
        repairDetails.sort(Comparator.comparing(RepairDetail::getCreateTime));
        repairDetails.forEach(
                repairDetail -> {
                    if (!map.containsKey(repairDetail.getDeviceId())){
                     map.put(repairDetail.getDeviceId(),repairDetail);
                    }
                }
        );

        map.forEach(
                (k,v)->{
                    List<BillVo> billVos = new ArrayList<>();
                    BillVo billVo = new BillVo();
                    billVo.setRepairBillId(v.getDeviceRepairBillId());
                    billVo.setDevicesId(v.getDeviceId());
                    billVo.setRepairDetailId(v.getId());
                    //根据装备id查询装备
                    DeviceLibrary library = deviceLibraryService.getOne(v.getDeviceId());
                    DeviceLibrary deviceLibrary = library.setConfigName();
                    BeanUtils.copyProperties(deviceLibrary,billVo);
                    //通过账单id和businessType查询task对象
                    //过滤出状态是 StatusEnum.REVOKEALLOTTASK.id
                    List<Task> taskList = taskService.findByBillIdAndBusinessType(v.getDeviceRepairBillId(), BusinessEnum.REPAIR.id);
                    List<Task> tasks = taskList.stream().filter(task -> task.getParentTaskId() == null).collect(Collectors.toList());
                    ClearTaskVo clearTaskVo = new ClearTaskVo();
                    clearTaskVo.setTaskId(tasks.get(0).getId());
                    billVos.add(billVo);
                    clearTaskVo.setBillVos(billVos);
                    Optional<RepairBill> byId = repairBillDao.findById(v.getDeviceRepairBillId());
                    byId.ifPresent(
                            clearTaskVo::setRepairBill
                    );
                    byId.ifPresent(
                            repairBill -> {
                                clearTaskVo.setRepairSendBill(repairSendBillDao.findByDeviceRepairBillId(repairBill.getId()));
                            }
                    );
                    clearTaskVos.add(clearTaskVo);
                }
        );
        clearTaskVos.forEach(
                clearTaskVo -> {
                    if (!map1.containsKey(clearTaskVo.getTaskId())){
                        map1.put(clearTaskVo.getTaskId(),clearTaskVo);
                    }else {
                        ClearTaskVo clearTaskVo1 = map1.get(clearTaskVo.getTaskId());
                        List<BillVo> billVos1 = clearTaskVo1.getBillVos();
                        billVos1.addAll(clearTaskVo.getBillVos());
                        clearTaskVo1.setBillVos(billVos1);
                        map1.put(clearTaskVo.getTaskId(),clearTaskVo1);
                    }
                }
        );
        return map1;
    }

//    @Override
//    public Map<Integer, ClearTaskVo> getClearTaskVo(List<Integer> deviceIds) {
//        List<ClearTaskVo> clearTaskVos = new ArrayList<>();
//        Map<Integer,ClearTaskVo> map1 = new HashMap<>();
//        //通过装备id查询出账单id (送修单repairBill)
//        ClearRepairVo clearRepairVo = underRepair(deviceIds);
//        List<RepairDetail> endRepairList = clearRepairVo.getEndRepairList();
//        if (endRepairList.size()>0){
//            //取出装备id相同的最新时间
//            Map<Integer, RepairDetail> map = new HashMap<>();
//            endRepairList.forEach(
//                    repairDetail -> {
//                        if (!map.containsKey(repairDetail.getDeviceId())) {
//                            map.put(repairDetail.getDeviceId(), repairDetail);
//                        }
//                    }
//            );
//            List<BillVo> billVos = new ArrayList<>();
//            map.forEach(
//                    (k, v) -> {
//                        BillVo billVo = new BillVo();
//                        billVo.setRepairBillId(v.getDeviceRepairBillId());
//                        billVo.setDevicesId(v.getDeviceId());
//                        billVo.setRepairDetailId(v.getId());
//                        //根据装备id查询装备
//                        DeviceLibrary library = deviceLibraryService.getOne(v.getDeviceId());
//                        DeviceLibrary deviceLibrary = library.setConfigName();
//                        BeanUtils.copyProperties(deviceLibrary, billVo);
//                        //通过账单id和businessType查询task对象
//                        //过滤出状态是 StatusEnum.REVOKEALLOTTASK.id
//                        List<Task> taskList = taskService.findByBillIdAndBusinessType(v.getDeviceRepairBillId(), BusinessEnum.REPAIR.id);
//                        List<Task> tasks = taskList.stream().filter(task -> task.getParentTaskId() == null).collect(Collectors.toList());
//                        ClearTaskVo clearTaskVo = new ClearTaskVo();
//                        clearTaskVo.setTaskId(tasks.get(0).getId());
//                        clearTaskVo.setBusinessType(BusinessEnum.REPAIR.id);
//                        billVo.setBillStatus(tasks.get(0).getBillStatus());
//                        billVos.add(billVo);
//                        clearTaskVo.setBillVos(billVos);
//                        clearTaskVos.add(clearTaskVo);
//                    }
//            );
//            clearTaskVos.forEach(
//                    clearTaskVo -> {
//                        if (!map1.containsKey(clearTaskVo.getTaskId())) {
//                            map1.put(clearTaskVo.getTaskId(), clearTaskVo);
//                        } else {
//                            ClearTaskVo clearTaskVo1 = map1.get(clearTaskVo.getTaskId());
//                            List<BillVo> billVos1 = clearTaskVo1.getBillVos();
//                            billVos1.addAll(clearTaskVo.getBillVos());
//                            clearTaskVo1.setBillVos(billVos1);
//                            map1.put(clearTaskVo.getTaskId(), clearTaskVo1);
//                        }
//                    }
//            );
//            return map1;
//        }else {
//            return null;
//        }
//    }

    @Override
    public void setDevicesOwnUnit(List<BillVo> billVos) {
        //待维修:
        //1.下级送修，本级未入库，则需要先将入库任务完成则变成待维修  入库任务完成后则可以将装备的所属改为自己的 装备状态为在库
        //2.本级的待维修 直接修改装备为在库
        //修改装备的所属以及备注
        //1 通过装备id修改装备的所属
        //1.2 获取装备的id
        List<Integer> deviceIds = billVos.stream().map(BillVo::getDevicesId).collect(Collectors.toList());
        //修改装备的所属和装备的状态
        deviceLibraryService.updateDevicesOwnUnit(deviceIds);
        Integer userId = userUtils.getCurrentUserId();
        //添加装备履历
        deviceIds.forEach(
                integer -> {
                    DeviceLogDto deviceLogDto = new DeviceLogDto(integer, "由于进行清退任务，该设备状态修改为在库", null, userId);
                    deviceLogService.addLog(deviceLogDto);
                }
        );
        //修改维修单的remark
        List<Integer> repairBillIds = billVos.stream().map(BillVo::getRepairBillId).collect(Collectors.toList());
        //根据sendBill查询
//        List<RepairSendBill> repairBills = new ArrayList<>();
        String remark = "装备正在进行清退处理,维修任务终止";
        repairBillIds.forEach(integer -> {
            RepairSendBill sendBill = repairSendBillDao.findByDeviceRepairBillId(integer);
            String scriptJson = sendBill.getScriptJson();
            List<ScriptSaveVo> scriptSaveVos = JacksonUtil.readValue(scriptJson, new TypeReference<List<ScriptSaveVo>>() {
            });
            for (ScriptSaveVo scriptSaveVo : scriptSaveVos) {
                if (deviceIds.contains(scriptSaveVo.getId())) {
                    scriptSaveVo.setRemark(remark);
                }
            }
            sendBill.setScriptJson(JacksonUtil.toJSon(scriptSaveVos));
            repairSendBillDao.save(sendBill);
        });
        List<Integer> repairDetailIds = billVos.stream().map(BillVo::getRepairDetailId).collect(Collectors.toList());
        //删除维修详情
        repairDetailIds.forEach(integer -> {
            deviceRepairDetailDao.deleteById(integer);
        });

    }

    /**
     * 查询哪些装备正在运输中(出库方已发起，但是入库方未入库) 装备是否在中办
     */
    @Override
    public ClearRepairVo underRepair(List<Integer> deviceIds){
        ClearRepairVo clearRepairVo = new ClearRepairVo();
        //通过装备id查询出账单id (送修单repairBill)
        List<RepairDetail> repairDetails = deviceRepairDetailDao.findAllByDeviceIdIn(deviceIds);
        //过滤出装备在中办的
        List<Units> superiorUnitsList = unitsService.findSuperiorUnitsList();
        List<String> names = superiorUnitsList.stream().map(Units::getName).collect(Collectors.toList());
//        List<RepairDetail> exceptionList = repairDetails.stream().filter(repairDetail -> names.contains(repairDetail.getLocationUnit())).sorted(Comparator.comparing(RepairDetail::getCreateTime)).collect(Collectors.toList());
        List<RepairDetail> exceptionList = repairDetails.stream().filter(repairDetail ->
                names.contains(unitsService.findById(repairDetail.getLocationUnitId()).getName()))
                .sorted(Comparator.comparing(RepairDetail::getCreateTime)).collect(Collectors.toList());
        repairDetails.removeAll(exceptionList);
        //维修未入库   等待维修分为接收区装备和市发起维修
//        List<RepairDetail> repairIngList = repairDetails.stream().filter(repairDetail -> Objects.equals(repairDetail.getRepairStatus(), RepairStatusEnum.WAIT_REPAIR.id)).sorted(Comparator.comparing(RepairDetail::getCreateTime)).collect(Collectors.toList());
        List<Integer> repairStatus1 = new ArrayList<>(Collections.singletonList(RepairStatusEnum.WAIT_REPAIR.id));
        List<RepairDetail> repairIngList = repairDetails.stream().filter(repairDetail -> repairStatus1.contains(repairDetail.getRepairStatus()))
                .sorted(Comparator.comparing(RepairDetail::getCreateTime)).collect(Collectors.toList());
        //维修已入库
        List<Integer> repairStatus = new ArrayList<>(Arrays.asList(RepairStatusEnum.REPAIRING.id, RepairStatusEnum.WAIT_SEND.id,RepairStatusEnum.WAIT_RECEIVE.id,RepairStatusEnum.RECEIVE_WAIT_STORAGE.id));
        List<RepairDetail> endRepairList = repairDetails.stream().filter(repairDetail -> repairStatus.contains(repairDetail.getRepairStatus())).sorted(Comparator.comparing(RepairDetail::getCreateTime)).collect(Collectors.toList());
        clearRepairVo.setExceptionList(exceptionList);
        clearRepairVo.setRepairingList(repairIngList);
        clearRepairVo.setEndRepairList(endRepairList);
        return clearRepairVo;
    }
}
