package cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorderbom;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.pms.controller.admin.iotdevicematerial.vo.IotDeviceMaterialRespVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotdevicerunlog.vo.IotDeviceRunLogRespVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorderbom.vo.IotMainWorkOrderBomPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorderbom.vo.IotMainWorkOrderBomRespVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorderbom.vo.IotMainWorkOrderBomSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDeviceRespVO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotmainworkorder.IotMainWorkOrderDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotmainworkorderbom.IotMainWorkOrderBomDO;
import cn.iocoder.yudao.module.pms.service.IotDeviceService;
import cn.iocoder.yudao.module.pms.service.iotdevicematerial.IotDeviceMaterialService;
import cn.iocoder.yudao.module.pms.service.iotdevicerunlog.IotDeviceRunLogService;
import cn.iocoder.yudao.module.pms.service.iotmainworkorder.IotMainWorkOrderService;
import cn.iocoder.yudao.module.pms.service.iotmainworkorderbom.IotMainWorkOrderBomService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;

import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;

@Tag(name = "管理后台 - PMS 保养工单明细BOM")
@RestController
@RequestMapping("/pms/iot-main-work-order-bom")
@Validated
public class IotMainWorkOrderBomController {

    @Resource
    private IotMainWorkOrderBomService iotMainWorkOrderBomService;
    @Resource
    private IotDeviceService iotDeviceService;
    @Resource
    private IotDeviceRunLogService iotDeviceRunLogService;
    @Resource
    private IotMainWorkOrderService iotMainWorkOrderService;
    @Resource
    private IotDeviceMaterialService iotDeviceMaterialService;

    @PostMapping("/create")
    @Operation(summary = "创建PMS 保养计划明细BOM")
    @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order-bom:create')")
    public CommonResult<Long> createIotMainWorkOrderBom(@Valid @RequestBody IotMainWorkOrderBomSaveReqVO createReqVO) {
        return success(iotMainWorkOrderBomService.createIotMainWorkOrderBom(createReqVO));
    }

    @PutMapping("/update")
    @Operation(summary = "更新PMS 保养计划明细BOM")
    @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order-bom:update')")
    public CommonResult<Boolean> updateIotMainWorkOrderBom(@Valid @RequestBody IotMainWorkOrderBomSaveReqVO updateReqVO) {
        iotMainWorkOrderBomService.updateIotMainWorkOrderBom(updateReqVO);
        return success(true);
    }

    @DeleteMapping("/delete")
    @Operation(summary = "删除PMS 保养计划明细BOM")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order-bom:delete')")
    public CommonResult<Boolean> deleteIotMainWorkOrderBom(@RequestParam("id") Long id) {
        iotMainWorkOrderBomService.deleteIotMainWorkOrderBom(id);
        return success(true);
    }

    @GetMapping("/get")
    @Operation(summary = "获得PMS 保养计划明细BOM")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order-bom:query')")
    public CommonResult<IotMainWorkOrderBomRespVO> getIotMainWorkOrderBom(@RequestParam("id") Long id) {
        IotMainWorkOrderBomDO iotMainWorkOrderBom = iotMainWorkOrderBomService.getIotMainWorkOrderBom(id);
        return success(BeanUtils.toBean(iotMainWorkOrderBom, IotMainWorkOrderBomRespVO.class));
    }

    @GetMapping("/page")
    @Operation(summary = "获得PMS 保养计划明细BOM分页")
    @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order-bom:query')")
    public CommonResult<PageResult<IotMainWorkOrderBomRespVO>> getIotMainWorkOrderBomPage(@Valid IotMainWorkOrderBomPageReqVO pageReqVO) {
        PageResult<IotMainWorkOrderBomDO> pageResult = iotMainWorkOrderBomService.getIotMainWorkOrderBomPage(pageReqVO);
        return success(BeanUtils.toBean(pageResult, IotMainWorkOrderBomRespVO.class));
    }

    @GetMapping("/getWorkOrderBOMs")
    @Operation(summary = "获得PMS 保养工单明细BOM列表")
    @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order-bom:query')")
    public CommonResult<List<IotMainWorkOrderBomRespVO>> getWorkOrderBOMs(@Valid IotMainWorkOrderBomPageReqVO reqVO) {
        List<IotMainWorkOrderBomDO> workOrderBOMs = iotMainWorkOrderBomService.getIotMainWorkOrderBomList(reqVO);
        // 组装bom关联的设备名称 编码
        // 组装bom关联的设备 累计运行时间、累计运行公里数
        return success(buildMainWorkOrderBomList(workOrderBOMs));
    }

    @GetMapping("/maintenanceCumulativeValue")
    @Operation(summary = "获得PMS 保养工单明细BOM列表 保养时累计公里数 时长")
    @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order-bom:query')")
    public CommonResult<List<IotMainWorkOrderBomRespVO>> maintenanceCumulativeValue(@Valid IotMainWorkOrderBomPageReqVO reqVO) {
        List<IotMainWorkOrderBomDO> workOrderBOMs = iotMainWorkOrderBomService.getIotMainWorkOrderBomList(reqVO);
        // 组装bom关联的设备 保养时累计公里数 时长
        return success(buildMaintenanceCumulativeValue(workOrderBOMs, reqVO.getActualStartTime()));
    }

    @GetMapping("/export-excel")
    @Operation(summary = "导出PMS 保养计划明细BOM Excel")
    @PreAuthorize("@ss.hasPermission('pms:iot-main-work-order-bom:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportIotMainWorkOrderBomExcel(@Valid IotMainWorkOrderBomPageReqVO pageReqVO,
              HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<IotMainWorkOrderBomDO> list = iotMainWorkOrderBomService.getIotMainWorkOrderBomPage(pageReqVO).getList();
        // 导出 Excel
        ExcelUtils.write(response, "PMS 保养计划明细BOM.xls", "数据", IotMainWorkOrderBomRespVO.class,
                        BeanUtils.toBean(list, IotMainWorkOrderBomRespVO.class));
    }

    private List<IotMainWorkOrderBomRespVO> buildMainWorkOrderBomList(List<IotMainWorkOrderBomDO> BOMs) {
        if (CollUtil.isEmpty(BOMs)) {
            return Collections.emptyList();
        }
        // 保养工单都是基于单设备 查询 保养工单主表信息 获取 保养开始时间
        // 如果 保养开始时间 有值，根据保养开始时间查询对应的保养时累计时长/里程

        // 组装bom关联的设备信息
        Map<Long, IotDeviceRespVO> deviceMap = iotDeviceService.getDeviceMap(convertListByFlatMap(BOMs,
                bom -> Stream.of(bom.getDeviceId())));

        // 查询保养工单明细中继承自保养计划明细 的 code累计运行时长属性名称 type累计运行公里数属性名称
        // 所有保养工单明细 已经 绑定了多个累计属性名称集合
        Set<String> pointNames = new HashSet<>();
        // 所有保养工单明细包含的设备id集合
        Set<Long> deviceIds = new HashSet<>();
        Set<Long> deviceCategoryIds = new HashSet<>();
        Map<Long, Long> deviceCategoryPair = new HashMap<>();
        // 保养j明细 保养项BOM节点id集合
        Set<Long> bomNodeIds = new HashSet<>();
        // 设备所属部门id集合
        Set<Long> deptIds = new HashSet<>();
        AtomicReference<Long> workOrderId = new AtomicReference<>();    // 保养工单id
        BOMs.forEach(bom -> {
            if (ObjUtil.isNotEmpty(bom.getDeviceId())) {
                deviceIds.add(bom.getDeviceId());
            }
            // 累计运行时长属性名称
            if (StrUtil.isNotBlank(bom.getCode())) {
                pointNames.add(bom.getCode());
            }
            // 累计运行公里数属性名称
            if (StrUtil.isNotBlank(bom.getType())) {
                pointNames.add(bom.getType());
            }
            bomNodeIds.add(bom.getBomNodeId());
            // 组装bom关联的设备信息
            if (CollUtil.isNotEmpty(deviceMap)) {
                deviceMap.forEach((k,v) -> {
                    deviceCategoryIds.add(v.getAssetClass());
                    deviceCategoryPair.put(k, v.getAssetClass());
                    deptIds.add(v.getDeptId());
                });
            }
            workOrderId.set(bom.getWorkOrderId());
        });
        // 如果 保养开始时间 有值，根据保养开始时间查询对应的保养时累计时长/里程
        IotMainWorkOrderDO workOrder = iotMainWorkOrderService.getIotMainWorkOrder(workOrderId.get());
        Map<Long, IotDeviceRunLogRespVO> specifiedDateDeviceRunLogMap = new HashMap<>();
        if (ObjUtil.isNotEmpty(workOrder.getActualStartTime())) {
            // 定义格式化器（线程安全，可直接复用）
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            // 转换 LocalDateTime 为指定格式的字符串
            String actualStartDate = workOrder.getActualStartTime().format(formatter);
            specifiedDateDeviceRunLogMap = iotDeviceRunLogService.getDeviceRunLogMapAlone(convertListByFlatMap(BOMs,
                    bom -> Stream.of(bom.getDeviceId())), new ArrayList<>(deviceCategoryIds), deviceCategoryPair, actualStartDate);
        }

        Map<Long, IotDeviceRunLogRespVO> deviceRunLogMap = iotDeviceRunLogService.getDeviceRunLogMapAlone(convertListByFlatMap(BOMs,
                bom -> Stream.of(bom.getDeviceId())), new ArrayList<>(deviceCategoryIds), deviceCategoryPair, null);
        // 查询 运行记录模板中包含多个累计 时长 公里数 属性的集合
        List<IotDeviceRunLogRespVO> multipleAccumulatedData = new ArrayList<>();
        // key(设备id-累计时长属性名称)    value多累计时长属性运行记录填报数值
        Map<String, BigDecimal> tempTotalRunDataPair = new HashMap<>();
        // 查询指定设备 指定 多累计属性名称 的设备运行记录数据集合数据
        if (CollUtil.isNotEmpty(deviceIds) && CollUtil.isNotEmpty(pointNames)) {
            multipleAccumulatedData = iotDeviceRunLogService.multipleAccumulatedData(deviceIds, pointNames, null);
        }
        if (CollUtil.isNotEmpty(multipleAccumulatedData)) {
            multipleAccumulatedData.forEach(data -> {
                String uniqueKey = StrUtil.join("-", data.getDeviceId(), data.getPointName());
                tempTotalRunDataPair.put(uniqueKey, data.getTotalRunTime());
            });
        }

        // 查询所有保养项 历史保养记录 (保养项所有历史工单中最新的保养时间)
        Map<String, LocalDateTime> latestMaintenanceDatePair = new HashMap<>();
        if (CollUtil.isNotEmpty(deviceIds) && CollUtil.isNotEmpty(bomNodeIds)) {
            List<IotMainWorkOrderBomDO> latestWorkOrderBomS = iotMainWorkOrderService.historyWorkOrderBoms(deviceIds, bomNodeIds);
            if (CollUtil.isNotEmpty(latestWorkOrderBomS)) {
                latestWorkOrderBomS.forEach(bom -> {
                    String uniqueKey = StrUtil.join("-", bom.getDeviceId(), bom.getBomNodeId());
                    latestMaintenanceDatePair.put(uniqueKey, bom.getUpdateTime());
                });
            }
        }
        // key设备id-bom节点id      value设备物料集合
        Map<String, List<IotDeviceMaterialRespVO>> deviceBomMaterialPair = iotDeviceMaterialService.bomMaterials(deptIds, deviceIds, bomNodeIds);

        Map<Long, IotDeviceRunLogRespVO> finalSpecifiedDateDeviceRunLogMap = specifiedDateDeviceRunLogMap;
        return BeanUtils.toBean(BOMs, IotMainWorkOrderBomRespVO.class, bomVO -> {
            // 设置设备相关信息
            MapUtils.findAndThen(deviceMap, bomVO.getDeviceId(),
                    device -> bomVO.setDeviceName(device.getDeviceName()));
            MapUtils.findAndThen(deviceMap, bomVO.getDeviceId(),
                    device -> bomVO.setDeviceCode(device.getDeviceCode()));

            MapUtils.findAndThen(finalSpecifiedDateDeviceRunLogMap, bomVO.getDeviceId(),
                    device -> bomVO.setMainRuntime(device.getTotalRunTime()));
            MapUtils.findAndThen(finalSpecifiedDateDeviceRunLogMap, bomVO.getDeviceId(),
                    device -> bomVO.setMainMileage(device.getTotalMileage()));

            MapUtils.findAndThen(deviceRunLogMap, bomVO.getDeviceId(),
                    device -> bomVO.setTotalMileage(device.getTotalMileage()));
            MapUtils.findAndThen(deviceRunLogMap, bomVO.getDeviceId(),
                    device -> bomVO.setTotalRunTime(device.getTotalRunTime()));

            // 运行记录模板中包含的多个累计类型属性
            if (StrUtil.isNotBlank(bomVO.getCode())) {
                // code 累计运行时长属性名称
                String uniqueKey = StrUtil.join("-", bomVO.getDeviceId(), bomVO.getCode());
                if (tempTotalRunDataPair.containsKey(uniqueKey)) {
                    bomVO.setTempTotalRunTime(tempTotalRunDataPair.get(uniqueKey));
                }
            }
            if (StrUtil.isNotBlank(bomVO.getType())) {
                // type 累计运行公里数属性名称
                String uniqueKey = StrUtil.join("-", bomVO.getDeviceId(), bomVO.getType());
                if (tempTotalRunDataPair.containsKey(uniqueKey)) {
                    bomVO.setTempTotalMileage(tempTotalRunDataPair.get(uniqueKey));
                }
            }
            // 设置保养项的上次保养时间
            String uniqueKey = StrUtil.join("-", bomVO.getDeviceId(), bomVO.getBomNodeId());
            if (latestMaintenanceDatePair.containsKey(uniqueKey)) {
                bomVO.setLastMaintenanceDate(latestMaintenanceDatePair.get(uniqueKey));
            }
            if (deviceBomMaterialPair.containsKey(uniqueKey)) {
                bomVO.setDeviceBomMaterials(deviceBomMaterialPair.get(uniqueKey));
            }
        });
    }

    /**
     * 保养工单填报 查询各保养项指定时间的累计运行里程 时长 前端根据 bomNodeId 匹配
     * @param BOMs
     * @return
     */
    private List<IotMainWorkOrderBomRespVO> buildMaintenanceCumulativeValue(List<IotMainWorkOrderBomDO> BOMs, String actualStartTime) {
        if (CollUtil.isEmpty(BOMs) || StrUtil.isBlank(actualStartTime)) {
            return Collections.emptyList();
        }
        // 组装bom关联的设备信息
        Map<Long, IotDeviceRespVO> deviceMap = iotDeviceService.getDeviceMap(convertListByFlatMap(BOMs,
                bom -> Stream.of(bom.getDeviceId())));

        // 查询保养工单明细中继承自保养计划明细 的 code累计运行时长属性名称 type累计运行公里数属性名称
        // 所有保养工单明细 已经 绑定了多个累计属性名称集合
        Set<String> pointNames = new HashSet<>();
        // 所有保养工单明细包含的设备id集合
        Set<Long> deviceIds = new HashSet<>();
        Set<Long> deviceCategoryIds = new HashSet<>();
        Map<Long, Long> deviceCategoryPair = new HashMap<>();
        // 保养j明细 保养项BOM节点id集合
        Set<Long> bomNodeIds = new HashSet<>();
        // 设备所属部门id集合
        Set<Long> deptIds = new HashSet<>();
        BOMs.forEach(bom -> {
            if (ObjUtil.isNotEmpty(bom.getDeviceId())) {
                deviceIds.add(bom.getDeviceId());
            }
            // 累计运行时长属性名称
            if (StrUtil.isNotBlank(bom.getCode())) {
                pointNames.add(bom.getCode());
            }
            // 累计运行公里数属性名称
            if (StrUtil.isNotBlank(bom.getType())) {
                pointNames.add(bom.getType());
            }
            bomNodeIds.add(bom.getBomNodeId());
            // 组装bom关联的设备信息
            if (CollUtil.isNotEmpty(deviceMap)) {
                deviceMap.forEach((k,v) -> {
                    deviceCategoryIds.add(v.getAssetClass());
                    deviceCategoryPair.put(k, v.getAssetClass());
                    deptIds.add(v.getDeptId());
                });
            }
        });
        Map<Long, IotDeviceRunLogRespVO> deviceRunLogMap = iotDeviceRunLogService.getDeviceRunLogMapAlone(convertListByFlatMap(BOMs,
                bom -> Stream.of(bom.getDeviceId())), new ArrayList<>(deviceCategoryIds), deviceCategoryPair, actualStartTime);
        // 查询 运行记录模板中包含多个累计 时长 公里数 属性的集合
        List<IotDeviceRunLogRespVO> multipleAccumulatedData = new ArrayList<>();
        // key(设备id-累计时长属性名称)    value多累计时长属性运行记录填报数值
        Map<String, BigDecimal> tempTotalRunDataPair = new HashMap<>();
        // 查询指定设备 指定 多累计属性名称 的设备运行记录数据集合数据
        if (CollUtil.isNotEmpty(deviceIds) && CollUtil.isNotEmpty(pointNames)) {
            multipleAccumulatedData = iotDeviceRunLogService.multipleAccumulatedData(deviceIds, pointNames, actualStartTime);
        }
        if (CollUtil.isNotEmpty(multipleAccumulatedData)) {
            multipleAccumulatedData.forEach(data -> {
                String uniqueKey = StrUtil.join("-", data.getDeviceId(), data.getPointName());
                tempTotalRunDataPair.put(uniqueKey, data.getTotalRunTime());
            });
        }

        return BeanUtils.toBean(BOMs, IotMainWorkOrderBomRespVO.class, bomVO -> {
            MapUtils.findAndThen(deviceRunLogMap, bomVO.getDeviceId(),
                    device -> bomVO.setMainMileage(device.getTotalMileage()));
            MapUtils.findAndThen(deviceRunLogMap, bomVO.getDeviceId(),
                    device -> bomVO.setMainRuntime(device.getTotalRunTime()));

            // 运行记录模板中包含的多个累计类型属性
            if (StrUtil.isNotBlank(bomVO.getCode())) {
                // code 累计运行时长属性名称
                String uniqueKey = StrUtil.join("-", bomVO.getDeviceId(), bomVO.getCode());
                if (tempTotalRunDataPair.containsKey(uniqueKey)) {
                    bomVO.setTempMainRunTime(tempTotalRunDataPair.get(uniqueKey));
                }
            }
            if (StrUtil.isNotBlank(bomVO.getType())) {
                // type 累计运行公里数属性名称
                String uniqueKey = StrUtil.join("-", bomVO.getDeviceId(), bomVO.getType());
                if (tempTotalRunDataPair.containsKey(uniqueKey)) {
                    bomVO.setTempMainMileage(tempTotalRunDataPair.get(uniqueKey));
                }
            }
        });
    }
}