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

import cn.hutool.core.collection.CollUtil;
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.security.core.LoginUser;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
import com.zt.plat.module.qms.common.dic.controller.vo.DictionaryBusinessRespVO;
import com.zt.plat.module.qms.common.dic.service.DictionaryBusinessService;
import com.zt.plat.module.qms.core.constant.CommonConstant;
import com.zt.plat.module.qms.core.constant.DataTypeConstant;
import com.zt.plat.module.qms.resource.material.constant.MaterialConstants;
import com.zt.plat.module.qms.resource.material.controller.vo.*;
import com.zt.plat.module.qms.resource.material.dal.dataobject.*;
import com.zt.plat.module.qms.resource.material.dal.mapper.MaterialInventoryInboundMapper;
import com.zt.plat.module.qms.resource.material.enums.MaterialAcceptStatus;
import com.zt.plat.module.qms.resource.material.enums.MaterialInboundType;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
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.Collections;
import java.util.List;

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


/**
 * 入库 Service 实现类
 *
 * @author 后台管理
 */
@Service
@Validated
public class MaterialInventoryInboundServiceImpl implements MaterialInventoryInboundService {

    @Resource
    private MaterialInventoryInboundMapper materialInventoryInboundMapper;
    @Autowired
    private MaterialBatchService materialBatchService;
    @Autowired
    private MaterialProductService materialProductService;
    @Autowired
    private MaterialInfomationService materialInfomationService;
    @Autowired
    private MaterialInventoryInboundDetailService materialInventoryInboundDetailService;
    @Autowired
    private DictionaryBusinessService dictionaryBusinessService;


    @Transactional
    @Override
    public MaterialInventoryInboundRespVO createMaterialInventoryInbound(MaterialInventoryInboundSaveReqVO createReqVO) {
        Long gongduanId = createReqVO.getGongduanId();
        BigDecimal reqQuantity = createReqVO.getQuantity();
        if (reqQuantity.compareTo(BigDecimal.ZERO) <= 0) throw new ServiceException(1_032_160_000, "入库数量不能小于等于0");
        // 入库数量限制
        inboundQtyLimit(reqQuantity);
        MaterialBatchDO gongDO = materialBatchService.getMaterialBatch(gongduanId);
        if (gongDO == null) throw exception(MATERIAL_BATCH_GONG_NOT_EXISTS);
        // 1.检查工段是否已经验收
        if (!MaterialAcceptStatus.accepted.name().equals(gongDO.getAcceptanceStatus()))
            throw new ServiceException(1_032_160_000, "工段未验收，不能入库");

        // 2. 校验入库数量不超过工段可用数量（考虑锁定数量）
        BigDecimal availableQuantity = gongDO.getRemaineQuantity().subtract(gongDO.getLockQuantity());
        if (reqQuantity.compareTo(availableQuantity) > 0)
            throw new ServiceException(1_032_160_000, "入库数量不能大于批次工段可用数量");

        // 3.保存入库记录
        MaterialInventoryInboundDO inbound = getInbound(createReqVO, gongDO);
        materialInventoryInboundMapper.insert(inbound);
        // 4.生成物料实例
        Long productId = gongDO.getProductId();
        MaterialProductDO product = materialProductService.getMaterialProduct(productId);
        if (product == null || !DataTypeConstant.DATA_TYPE_DATA.equals(product.getNodeType()))
            throw exception(MATERIAL_PRODUCT_NOT_EXISTS);
        Long batchId = gongDO.getParentId();
        MaterialBatchDO batch = materialBatchService.getMaterialBatch(batchId);
        if (batch == null || batch.getParentId() != 0) throw exception(MATERIAL_BATCH_NOT_EXISTS);
        Long locationId = createReqVO.getLocationId();
        List<MaterialInfomationDO> infomationDOS = materialInfomationService.saveMaterialInfomationsByBatInb(locationId, reqQuantity, gongDO, product, batch);
        // 5.保存入库明细
        materialInventoryInboundDetailService.saveInboundDetails(infomationDOS, inbound, batchId, gongduanId);
        // 是否为检定入库
        Boolean isVerify = materialProductService.checkIsVerifyCategoryByPdtId(batch.getProductId());
        // 更新工段已入库数量和可用数量
        gongDO.setInboundEndQuantity(gongDO.getInboundEndQuantity().add(reqQuantity));
        gongDO.setRemaineQuantity(gongDO.getRemaineQuantity().subtract(reqQuantity));
        materialBatchService.updateById(gongDO);
        // 更新批次已入库数量和可用数量
        batch.setInboundEndQuantity(batch.getInboundEndQuantity().add(reqQuantity));
        if (!isVerify) {
            // 检定业务 检定流程提交时已经扣减过可用数量
            batch.setRemaineQuantity(batch.getRemaineQuantity().subtract(reqQuantity));
        }
        materialBatchService.updateById(batch);
        // 更新物料大类预警级别
        materialProductService.updateMaterialProductAlarmLevel(Collections.singletonList(productId));
        // 返回
        MaterialInventoryInboundRespVO respVO = BeanUtils.toBean(inbound, MaterialInventoryInboundRespVO.class);
        List<MaterialInventoryInboundDetailRespVO> detailRespVOList =
                materialInventoryInboundDetailService.getMaterialInfoListByInboundId(inbound.getId());
        respVO.setDetailList(detailRespVOList);
        /*List<Long> infIds = infomationDOS.stream().map(MaterialInfomationDO::getId).toList();
        List<MaterialInfomationRespVO> infs = materialInfomationService.getRespListWithPdtInfoByIds(infIds);
        respVO.setInfomationList(infs);*/
        return respVO;
    }

    @Override
    public void inboundQtyLimit(BigDecimal reqQuantity) {
        CommonResult<DictionaryBusinessRespVO> inboundQuantityLimitResult = dictionaryBusinessService.getDataByDataKey(MaterialConstants.DICT_MATERIAL_INBOUND_QUANTITY_LIMIT);
        DictionaryBusinessRespVO inboundQuantityLimit = inboundQuantityLimitResult.getData();
        if (reqQuantity.compareTo(CommonConstant.MAX_GENERATE_QUANTITY) > 0
                || reqQuantity.compareTo(CommonConstant.MAX_RESPONSE_QUANTITY) > 0)
            throw new ServiceException(1_032_160_000,
                    String.format("入库数量不能超过系统限制数量：%s", Math.min(CommonConstant.MAX_GENERATE_QUANTITY.doubleValue(), CommonConstant.MAX_RESPONSE_QUANTITY.doubleValue())));
        if (inboundQuantityLimit != null) {
            BigDecimal inbQtyLimit = new BigDecimal(inboundQuantityLimit.getValue());
            if (reqQuantity.compareTo(inbQtyLimit) > 0) {
                throw new ServiceException(1_032_160_000,
                        String.format("入库数量超出配置限制：%s，当前请求数量：%s", inbQtyLimit, reqQuantity));
            }
        }
    }

    @Override
    public void saveBatch(List<MaterialInventoryInboundDO> inbounds) {
        materialInventoryInboundMapper.insertBatch(inbounds);
    }

    @Override
    public List<MaterialInfomationDO> inboundInfomation(MaterialInventoryInboundDO inboundInfo, MaterialProductDO product, BigDecimal totalOperationQuantity, List<MaterialInfomationDO> infomations) {
        MaterialInboundType inboundType = MaterialInboundType.fromName(inboundInfo.getBusinessType());
        // 1. 保存入库单
        materialInventoryInboundMapper.insert(inboundInfo);
        if (inboundType == MaterialInboundType.hzrd_make_inbound) {
            MaterialInfomationDO infomation = materialInfomationService.saveMaterialInfomationByHzrdMtrlMake(product, totalOperationQuantity);
            MaterialInventoryInboundDetailDO detailDO = new MaterialInventoryInboundDetailDO()
                    .setInboundId(inboundInfo.getId())
                    .setMaterialInfomationId(infomation.getId())
                    .setInboundUserName(inboundInfo.getApplyUser())
                    .setInboundUserId(inboundInfo.getApplyUserId())
                    .setInboundDepartmentName(inboundInfo.getApplyDepartment())
                    .setInboundDepartmentId(inboundInfo.getApplyDepartmentId())
                    .setInboundTime(LocalDateTime.now())
                    .setRemark(inboundInfo.getRemark());
            materialInventoryInboundDetailService.saveBatch(List.of(detailDO));
            return List.of(infomation);
        } else if (inboundType == MaterialInboundType.check_inbound) {
            // 保存入库明细
            List<MaterialInventoryInboundDetailDO> inboundDetails = infomations.stream()
                    .map(inf -> new MaterialInventoryInboundDetailDO()
                            .setInboundId(inboundInfo.getId())
                            .setMaterialInfomationId(inf.getId())
                            .setInboundUserName(inboundInfo.getApplyUser())
                            .setInboundUserId(inboundInfo.getApplyUserId())
                            .setInboundDepartmentName(inboundInfo.getApplyDepartment())
                            .setInboundDepartmentId(inboundInfo.getApplyDepartmentId())
                            .setInboundTime(LocalDateTime.now())
                            .setRemark(inboundInfo.getRemark()))
                    .toList();
            materialInventoryInboundDetailService.saveBatch(inboundDetails);

            // 更新物料在库状态
            List<Long> infIds = infomations.stream().map(MaterialInfomationDO::getId).toList();
            materialInfomationService.updateByIds(infIds, new MaterialInfomationDO().setUsageStatus(0));
        }
        return List.of();
    }

    private MaterialInventoryInboundDO getInbound(MaterialInventoryInboundSaveReqVO createReqVO, MaterialBatchDO gongDO) {
        MaterialInventoryInboundDO inbound = BeanUtils.toBean(createReqVO, MaterialInventoryInboundDO.class);
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        if (loginUser == null) throw exception(USER_NOT_EXISTS);
        String loginUserNickname = SecurityFrameworkUtils.getLoginUserNickname();
        inbound.setApplyUser(loginUserNickname).setApplyUserId(loginUser.getId())
                .setApplyDepartment(loginUser.getVisitDeptName()).setApplyDepartmentId(loginUser.getVisitDeptId())
                .setApplyTime(LocalDateTime.now())
                .setRemark(gongDO.getRemark());
        return inbound;
    }

    @Override
    public void updateMaterialInventoryInbound(MaterialInventoryInboundSaveReqVO updateReqVO) {
        // 校验存在
        validateMaterialInventoryInboundExists(updateReqVO.getId());
        // 更新
        MaterialInventoryInboundDO updateObj = BeanUtils.toBean(updateReqVO, MaterialInventoryInboundDO.class);
        materialInventoryInboundMapper.updateById(updateObj);
    }

    @Override
    public void deleteMaterialInventoryInbound(Long id) {
        // 校验存在
        validateMaterialInventoryInboundExists(id);
        // 删除
        materialInventoryInboundMapper.deleteById(id);
    }

    @Override
        public void deleteMaterialInventoryInboundListByIds(List<Long> ids) {
        // 校验存在
        validateMaterialInventoryInboundExists(ids);
        // 删除
        materialInventoryInboundMapper.deleteByIds(ids);
        }

    private void validateMaterialInventoryInboundExists(List<Long> ids) {
        List<MaterialInventoryInboundDO> list = materialInventoryInboundMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(MATERIAL_INVENTORY_INBOUND_NOT_EXISTS);
        }
    }

    private void validateMaterialInventoryInboundExists(Long id) {
        if (materialInventoryInboundMapper.selectById(id) == null) {
            throw exception(MATERIAL_INVENTORY_INBOUND_NOT_EXISTS);
        }
    }

    @Override
    public MaterialInventoryInboundDO getMaterialInventoryInbound(Long id) {
        return materialInventoryInboundMapper.selectById(id);
    }

    @Override
    public PageResult<MaterialInventoryInboundDO> getMaterialInventoryInboundPage(MaterialInventoryInboundPageReqVO pageReqVO) {
        return materialInventoryInboundMapper.selectPage(pageReqVO);
    }

}