package cn.iocoder.yudao.module.pms.service.iotdeviceallotlog;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.pms.controller.admin.iotdeviceallotlog.vo.IotDeviceAllotLogPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotdeviceallotlog.vo.IotDeviceAllotLogSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotdeviceallotlog.vo.IotDeviceAllotSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotdeviceperson.vo.IotDevicePersonRelationSaveReqVO;
import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotdeviceallotlog.IotDeviceAllotLogDO;
import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotdeviceallotlog.IotDeviceAllotLogMapper;
import cn.iocoder.yudao.module.pms.service.IotTreeService;
import cn.iocoder.yudao.module.pms.service.failure.IotFailureReportService;
import cn.iocoder.yudao.module.pms.service.inspect.IotInspectOrderService;
import cn.iocoder.yudao.module.pms.service.inspect.IotInspectPlanService;
import cn.iocoder.yudao.module.pms.service.iotdeviceperson.IotDevicePersonService;
import cn.iocoder.yudao.module.pms.service.iotmainworkorder.IotMainWorkOrderService;
import cn.iocoder.yudao.module.pms.service.iotopeationfill.IotOpeationFillService;
import cn.iocoder.yudao.module.pms.service.maintain.IotMaintainService;
import cn.iocoder.yudao.module.pms.service.maintenance.IotMaintenancePlanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_DEVICE_ALLOT_LOG_NOT_EXISTS;
import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_MAINTAIN_RUNNING_EXISTS;
import static cn.iocoder.yudao.module.pms.framework.config.MultiThreadConfiguration.PMS_THREAD_POOL_TASK_EXECUTOR;

/**
 * 设备调拨日志 Service 实现类
 *
 * @author ruiqi
 */
@Service
@Validated
public class IotDeviceAllotLogServiceImpl implements IotDeviceAllotLogService {

    @Resource
    private IotDeviceAllotLogMapper iotDeviceAllotLogMapper;
    @Resource
    private IotDeviceMapper iotDeviceMapper;
    @Resource
    private IotDevicePersonService iotDevicePersonService;
    @Resource
    private IotOpeationFillService iotOpeationFillService;
    @Resource
    private IotFailureReportService iotFailureReportService;
    @Resource
    private IotMaintainService iotMaintainService;
    @Resource
    private IotInspectOrderService iotInspectOrderService;
    @Resource
    private IotMainWorkOrderService iotMainWorkOrderService;
    @Resource
    private IotMaintenancePlanService iotMaintenancePlanService;
    @Resource(name = PMS_THREAD_POOL_TASK_EXECUTOR)
    private ThreadPoolTaskExecutor pmsThreadPoolTaskExecutor;
    @Resource
    private IotTreeService iotTreeService;
    @Autowired
    private IotInspectPlanService iotInspectPlanService;

    @Override
    public Long createIotDeviceAllotLog(IotDeviceAllotLogSaveReqVO createReqVO) {
        // 插入
        IotDeviceAllotLogDO iotDeviceAllotLog = BeanUtils.toBean(createReqVO, IotDeviceAllotLogDO.class);
        iotDeviceAllotLogMapper.insert(iotDeviceAllotLog);
        // 返回
        return iotDeviceAllotLog.getId();
    }

    @Override
    public void updateIotDeviceAllotLog(IotDeviceAllotLogSaveReqVO updateReqVO) {
        // 校验存在
        validateIotDeviceAllotLogExists(updateReqVO.getId());
        // 更新
        IotDeviceAllotLogDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceAllotLogDO.class);
        iotDeviceAllotLogMapper.updateById(updateObj);
    }

    @Override
    public void deleteIotDeviceAllotLog(Long id) {
        // 校验存在
        validateIotDeviceAllotLogExists(id);
        // 删除
        iotDeviceAllotLogMapper.deleteById(id);
    }

    private void validateIotDeviceAllotLogExists(Long id) {
        if (iotDeviceAllotLogMapper.selectById(id) == null) {
            throw exception(IOT_DEVICE_ALLOT_LOG_NOT_EXISTS);
        }
    }

    @Override
    public IotDeviceAllotLogDO getIotDeviceAllotLog(Long id) {
        return iotDeviceAllotLogMapper.selectById(id);
    }

    @Override
    public PageResult<IotDeviceAllotLogDO> getIotDeviceAllotLogPage(IotDeviceAllotLogPageReqVO pageReqVO) {
        return iotDeviceAllotLogMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveDeviceAllot(List<IotDeviceAllotSaveReqVO> reqVOS) {
        // 批量保存设备的状态
        if (CollUtil.isEmpty(reqVOS)) {
            throw exception(IOT_DEVICE_ALLOT_LOG_NOT_EXISTS);
        }
        // 查询出所有要保存设备的id
        List<Long> deviceIds = new ArrayList<>();
        List<Long> changedDeviceIds = new ArrayList<>();
        reqVOS.forEach(req -> {
            deviceIds.add(req.getDeviceId());
        });
        List<IotDeviceDO> devices = iotDeviceMapper.selectList("id", deviceIds);
        Map<Long, IotDeviceDO> deviceMap = devices.stream()
                .collect(Collectors.toMap(IotDeviceDO::getId, device -> device));
        List<IotDeviceAllotLogDO> deviceAllotLogs = new ArrayList<>();
        List<IotDeviceDO> tobeUpdatedDevices = new ArrayList<>();
        List<IotDevicePersonRelationSaveReqVO> personRelations = new ArrayList<>();
        AtomicReference<Long> deptId = new AtomicReference<>(0L);
        // 设备对应 责任人姓名(逗号分隔)
        AtomicReference<String> devicePersonNames = new AtomicReference<>(StrUtil.EMPTY);
        // key设备id  value设备调拨前所属部门id
        Map<Long, Long> oldDeptPair = new HashMap<>();
        if (CollUtil.isNotEmpty(deviceMap)) {
            deviceMap.forEach((deviceId, device) -> {
                oldDeptPair.put(deviceId, device.getDeptId());
            });
        }
        // 如果选择了设备责任人 查询出所有责任人姓名
        reqVOS.forEach(req -> {
            deptId.set(req.getDeptId());
            if (deviceMap.containsKey(req.getDeviceId()) && !req.getDeptId().equals(deviceMap.get(req.getDeviceId()).getDeptId())) {
                // 如果同步选择了设备责任人 姓名也存在 将责任人姓名同步到 设备表 chargeName
                if (CollUtil.isNotEmpty(req.getPersonNames())) {
                    devicePersonNames.set(CollUtil.join(req.getPersonNames(), StrUtil.COMMA));
                }

                IotDeviceAllotLogDO tempDeviceAllot = new IotDeviceAllotLogDO();
                tempDeviceAllot.setDeviceId(req.getDeviceId());
                tempDeviceAllot.setOldDeptId(deviceMap.get(req.getDeviceId()).getDeptId());
                tempDeviceAllot.setNewDeptId(req.getDeptId());
                tempDeviceAllot.setReason(req.getReason());
                deviceAllotLogs.add(tempDeviceAllot);
                IotDeviceDO device = deviceMap.get(req.getDeviceId());
                device.setDeptId(req.getDeptId());
                device.setChargeName(devicePersonNames.get());
                tobeUpdatedDevices.add(device);
                // 只处理调拨前后部门不同的设备 相关历史数据
                changedDeviceIds.add(req.getDeviceId());
            }
            // 如果同步选择了设备责任人 需要同步修改相关设备的责任人并记录日志
            if (CollUtil.isNotEmpty(req.getPersonIds())) {
                IotDevicePersonRelationSaveReqVO tempReqVO = new IotDevicePersonRelationSaveReqVO();
                tempReqVO.setDeviceId(req.getDeviceId());
                tempReqVO.setUserIds(req.getPersonIds());
                tempReqVO.setReason(req.getReason());
                personRelations.add(tempReqVO);
            }

        });
        // 判断是否有正在进行的 维修工单 如果为false，不能调拨
        if (!iotMaintainService.ifFinished(changedDeviceIds)) {
            throw exception(IOT_MAINTAIN_RUNNING_EXISTS);
        }
        if (CollUtil.isNotEmpty(deviceAllotLogs)) {
            iotDeviceAllotLogMapper.insertBatch(deviceAllotLogs);
        }
        // 批量修改 设备的状态
        if (CollUtil.isNotEmpty(tobeUpdatedDevices)) {
            iotDeviceMapper.updateBatch(tobeUpdatedDevices);
        }
        // 如果选择了设备责任人 需要同步修改设备责任人并记录 日志
        if (CollUtil.isNotEmpty(personRelations)) {
            iotDevicePersonService.saveDevicePersons(personRelations);
        }
        pmsThreadPoolTaskExecutor.execute(() -> {
            // 异步修改 设备相关的 历史工单
            // 运行记录的历史工单
            iotOpeationFillService.syncWordOrderData(changedDeviceIds, null, deptId.get());
            // 故障上报历史工单部门
            iotFailureReportService.syncWordOrderData(changedDeviceIds, deptId.get());
            // 修改维修历史工单
            iotMaintainService.syncWordOrderData(changedDeviceIds, deptId.get());
            // 修改巡检历史工单
            iotInspectOrderService.syncWordOrderData(changedDeviceIds, deptId.get());
            //巡检计划调拨
            iotInspectPlanService.syncInspectPlanData(changedDeviceIds, deptId.get());
            if (CollUtil.isNotEmpty(reqVOS.get(0).getPersonIds())) {
                // 修改保养历史工单
                iotMainWorkOrderService.syncWorkOrderData(changedDeviceIds, deptId.get(), reqVOS.get(0).getPersonIds(), oldDeptPair);
                // 修改 执行中 的保养工单
                iotMainWorkOrderService.syncRunningWorkOrder(changedDeviceIds, deptId.get(), reqVOS.get(0).getPersonIds(), oldDeptPair);
            }
            // 修改 保养计划 单独处理保养计划中当前调拨设备对应的保养项
            changedDeviceIds.forEach(System.out::println);
            System.out.println("部门id：" + deptId.get());
            oldDeptPair.forEach((k, v) -> System.out.println(k + " -> " + v));
            iotMaintenancePlanService.syncMaintenancePlan(changedDeviceIds, deptId.get(), reqVOS.get(0).getPersonIds(), oldDeptPair);
            // 修改历史设备资料库
            iotTreeService.deviceAllotTree(changedDeviceIds, deptId.get());
        });
        return 1l;
    }

}