package com.zt.plat.module.qms.resource.material.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.mybatis.core.query.MPJLambdaWrapperX;
import com.zt.plat.module.qms.resource.material.controller.vo.MaterialInventoryOutboundPageReqVO;
import com.zt.plat.module.qms.resource.material.controller.vo.MaterialInventoryOutboundRespVO;
import com.zt.plat.module.qms.resource.material.controller.vo.MaterialInventoryOutboundSaveReqVO;
import com.zt.plat.module.qms.resource.material.dal.dataobject.*;
import com.zt.plat.module.qms.resource.material.dal.mapper.MaterialInventoryOutboundMapper;
import com.zt.plat.module.qms.resource.material.enums.MaterialFlowType;
import com.zt.plat.module.qms.resource.material.enums.MaterialOutboundType;
import com.zt.plat.module.system.api.dept.DeptApi;
import com.zt.plat.module.system.api.dept.dto.DeptRespDTO;
import com.zt.plat.module.system.api.user.AdminUserApi;
import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.module.qms.enums.ErrorCodeConstants.MATERIAL_INVENTORY_OUTBOUND_NOT_EXISTS;

/**
 * 物料出库 Service 实现类
 *
 * @author 后台管理
 */
@Slf4j
@Service
@Validated
public class MaterialInventoryOutboundServiceImpl implements MaterialInventoryOutboundService {

    @Resource
    private MaterialInventoryOutboundMapper materialInventoryOutboundMapper;

    @Autowired
    private AdminUserApi adminUserApi;
    @Autowired
    private DeptApi deptApi;
    @Autowired
    private MaterialInfomationService materialInfomationService;
    @Autowired
    private MaterialInventoryOutboundDetailService materialInventoryOutboundDetailService;
    @Autowired
    private MaterialProductService materialProductService;
    @Autowired
    private MaterialUseRecordService materialUseRecordService;

    @Transactional
    @Override
    public MaterialInventoryOutboundRespVO createMaterialInventoryOutbound(MaterialInventoryOutboundSaveReqVO createReqVO) {

        MaterialInventoryOutboundDO outboundDO = BeanUtils.toBean(createReqVO, MaterialInventoryOutboundDO.class);

        List<Long> infomationIds = createReqVO.getInfomationIds();
        if (CollUtil.isEmpty(infomationIds)) return BeanUtils.toBean(outboundDO, MaterialInventoryOutboundRespVO.class);
        List<MaterialInfomationDO> infs = materialInfomationService.getListByIds(infomationIds);
        if (CollUtil.isEmpty(infs) || infs.size() != infomationIds.size())
            throw new ServiceException(1_032_160_000, "物料信息不存在或与传入的数量不匹配");
        for (MaterialInfomationDO inf : infs) {
            if (inf.getUsageStatus() == 1) throw new ServiceException(1_032_160_000, "物料【"+ inf.getName() + ":" + inf.getCode() +"】已出库");
        }
        // 获取申请人信息
        Long applyUserId = createReqVO.getApplyUserId();
        CommonResult<AdminUserRespDTO> userResult = adminUserApi.getUser(applyUserId);
        AdminUserRespDTO applyUser = userResult.getData();
        CommonResult<DeptRespDTO> deptResult = deptApi.getDept(applyUser.getDeptId());
        DeptRespDTO applyDept = deptResult.getData();
        outboundDO.setApplyUserId(applyUserId).setApplyUser(applyUser.getNickname())
                        .setApplyDepartmentId(applyDept.getId()).setApplyDepartment(applyDept.getName())
                        .setApplyTime(LocalDateTime.now());

        materialInventoryOutboundMapper.insert(outboundDO);
        // 保存出库明细
        List<MaterialInventoryOutboundDetailDO> outboundDetailDOS = infomationIds.stream().map(
                id -> new MaterialInventoryOutboundDetailDO()
                        .setOutboundId(outboundDO.getId())
                        .setInfomationId(id)
                        .setQuantity(BigDecimal.valueOf(1))
                        .setRemark(outboundDO.getRemark())).toList();
        materialInventoryOutboundDetailService.saveBatch(outboundDetailDOS);
        // 修改物料实例状态
        List<Long> infIds = infs.stream().map(MaterialInfomationDO::getId).toList();
        MaterialInfomationDO infomationUpdate = new MaterialInfomationDO()
                .setUsageStatus(1)
                .setManagerUserId(outboundDO.getApplyUserId()).setManagerUserName(outboundDO.getApplyUser())
                .setManagerDepartmentId(outboundDO.getApplyDepartmentId()).setManagerDepartmentName(outboundDO.getApplyDepartment());
        materialInfomationService.updateByIds(infIds, infomationUpdate);
        // 如果是危化品领用出库，保存使用记录
        List<Long> pdtIds = infs.stream().map(MaterialInfomationDO::getProductId).collect(Collectors.toSet()).stream().toList();
        if (MaterialOutboundType.receive_outbound.getName().equals(createReqVO.getBusinessType())) {
            List<MaterialProductDO> pdts = materialProductService.getListByIds(pdtIds);
            Map<Long, MaterialProductDO> productDOMapById = pdts.stream().collect(Collectors.toMap(MaterialProductDO::getId, Function.identity()));
            List<MaterialInfomationDO> hzrdInfs = infs.stream().filter(inf -> productDOMapById.get(inf.getProductId()).getHazardous().equals(1)).toList();
            if (CollUtil.isNotEmpty(hzrdInfs)) {
                materialUseRecordService.saveBatchByInfsOutb(outboundDO, hzrdInfs);
            }
        }

        // 更新大类预警级别
        materialProductService.updateMaterialProductAlarmLevel(pdtIds);
        return BeanUtils.toBean(outboundDO, MaterialInventoryOutboundRespVO.class);
    }

    @Override
    public void updateMaterialInventoryOutbound(MaterialInventoryOutboundSaveReqVO updateReqVO) {
        // 校验存在
        validateMaterialInventoryOutboundExists(updateReqVO.getId());
        // 更新
        MaterialInventoryOutboundDO updateObj = BeanUtils.toBean(updateReqVO, MaterialInventoryOutboundDO.class);
        materialInventoryOutboundMapper.updateById(updateObj);
    }

    @Override
    public void deleteMaterialInventoryOutbound(Long id) {
        // 校验存在
        validateMaterialInventoryOutboundExists(id);
        // 删除
        materialInventoryOutboundMapper.deleteById(id);
    }

    @Override
        public void deleteMaterialInventoryOutboundListByIds(List<Long> ids) {
        // 校验存在
        validateMaterialInventoryOutboundExists(ids);
        // 删除
        materialInventoryOutboundMapper.deleteByIds(ids);
        }

    private void validateMaterialInventoryOutboundExists(List<Long> ids) {
        List<MaterialInventoryOutboundDO> list = materialInventoryOutboundMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(MATERIAL_INVENTORY_OUTBOUND_NOT_EXISTS);
        }
    }

    private void validateMaterialInventoryOutboundExists(Long id) {
        if (materialInventoryOutboundMapper.selectById(id) == null) {
            throw exception(MATERIAL_INVENTORY_OUTBOUND_NOT_EXISTS);
        }
    }

    @Override
    public MaterialInventoryOutboundDO getMaterialInventoryOutbound(Long id) {
        return materialInventoryOutboundMapper.selectById(id);
    }

    @Override
    public PageResult<MaterialInventoryOutboundDO> getMaterialInventoryOutboundPage(MaterialInventoryOutboundPageReqVO pageReqVO) {
        return materialInventoryOutboundMapper.selectPage(pageReqVO);
    }

    @Override
    public List<MaterialInventoryOutboundRespVO> getUsageOutboundStatisticsByDept(MaterialInventoryOutboundPageReqVO reqVO) {
        List<MaterialInventoryOutboundRespVO> outboundRespVOS = materialInventoryOutboundMapper.selectListUsageStatisticsMainDataByDept(reqVO);
        if (CollUtil.isEmpty(outboundRespVOS)) return outboundRespVOS;
        List<Long> pdtIds = outboundRespVOS.stream().map(MaterialInventoryOutboundRespVO::getProductId).toList();
        List<MaterialProductDO> products = materialProductService.getListByIds(pdtIds);
        if (CollUtil.isEmpty(products)) return outboundRespVOS;
        Map<Long, MaterialProductDO> productMapById = products.stream().collect(Collectors.toMap(MaterialProductDO::getId, Function.identity()));
        for (MaterialInventoryOutboundRespVO outboundRespVO : outboundRespVOS) {
            MaterialProductDO productDO = productMapById.get(outboundRespVO.getProductId());
            outboundRespVO.setProductName(productDO.getName()).setProductCode(productDO.getCode())
                    .setProductModelNo(productDO.getModelNo()).setUnit(productDO.getUnit());
        }
        List<Long> deptIds = outboundRespVOS.stream().map(MaterialInventoryOutboundRespVO::getApplyDepartmentId).toList();
        MPJLambdaWrapper<MaterialInventoryOutboundDO> wrapper = new MPJLambdaWrapperX<MaterialInventoryOutboundDO>()
                .select(MaterialInventoryOutboundDO::getApplyDepartmentId)
                .select("MAX(t.ID) as ID")
                .in(MaterialInventoryOutboundDO::getApplyDepartmentId, deptIds)
                .groupBy(MaterialInventoryOutboundDO::getApplyDepartmentId);
        if (reqVO.getApplyTime() != null && reqVO.getApplyTime().length == 2)
            wrapper.between(MaterialInventoryOutboundDO::getApplyTime, reqVO.getApplyTime()[0], reqVO.getApplyTime()[1]);
        List<Map<String, Object>> maxIdMaps = materialInventoryOutboundMapper.selectMaps(wrapper);

        if (CollUtil.isEmpty(maxIdMaps)) return outboundRespVOS;
        // 提取每个部门的最大 ID
        List<Long> maxIds = maxIdMaps.stream()
                .map(map -> MapUtil.getLong(map, "ID"))
                .filter(Objects::nonNull)
                .toList();

        // 批量查询这些最新记录
        List<MaterialInventoryOutboundDO> outboundDOS = materialInventoryOutboundMapper.selectByIds(maxIds);
        Map<Long, MaterialInventoryOutboundDO> outboundMapByDeptId = outboundDOS.stream()
                .collect(Collectors.toMap(MaterialInventoryOutboundDO::getApplyDepartmentId, Function.identity()));
        for (MaterialInventoryOutboundRespVO outboundRespVO : outboundRespVOS) {
            MaterialInventoryOutboundDO outboundDO = outboundMapByDeptId.get(outboundRespVO.getApplyDepartmentId());
            outboundRespVO.setApplyUser(outboundDO.getApplyUser()).setApplyDepartment(outboundDO.getApplyDepartment())
                    .setApplyTime(outboundDO.getApplyTime()).setRemark(outboundDO.getRemark());
        }
        return outboundRespVOS;
    }

}