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

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
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.MaterialUseRecordMapper;
import com.zt.plat.module.qms.resource.material.enums.MaterialNormalOperationType;
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.*;
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_USE_RECORD_NOT_EXISTS;

/**
 * 使用记录 Service 实现类
 *
 * @author 后台管理
 */
@Service
@Validated
public class MaterialUseRecordServiceImpl implements MaterialUseRecordService {

    @Resource
    private MaterialUseRecordMapper materialUseRecordMapper;

    @Autowired
    private MaterialInfomationService materialInfomationService;
    @Autowired
    private MaterialUseEndReuseService materialUseEndReuseService;

    @Transactional
    @Override
    public MaterialUseRecordRespVO createMaterialUseRecord(MaterialUseRecordSaveReqVO createReqVO) {
        Long infomationId = createReqVO.getInfomationId();
        BigDecimal operationQuantity = createReqVO.getOperationQuantity();
        MaterialInfomationDO infomationDO = materialInfomationService.getMaterialInfomation(infomationId);
        if (infomationDO == null) throw new ServiceException(1_032_160_000, "物料实例不存在");
        if (infomationDO.getUseEndFlag() == 1) throw new ServiceException(1_032_160_000, "物料已用完");
        // 使用量不能大于剩余量
        BigDecimal remainingVolume = infomationDO.getRemainingVolume();

        if (operationQuantity.compareTo(remainingVolume) > 0) throw new ServiceException(1_032_160_000, "使用量不能大于剩余量");
        // 插入
        MaterialUseRecordDO recordDO = BeanUtils.toBean(createReqVO, MaterialUseRecordDO.class);

        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        String loginUserNickname = SecurityFrameworkUtils.getLoginUserNickname();

        recordDO.setUseDate(LocalDateTime.now()).setUserId(loginUserId).setUserName(loginUserNickname);
        recordDO.setBeforeQuantity(remainingVolume)
                .setAfterQuantity(remainingVolume.subtract(operationQuantity));
        materialUseRecordMapper.insert(recordDO);

        // 更新物料剩余量
        infomationDO.setRemainingVolume(remainingVolume.subtract(operationQuantity));
        // 物料用完
        if (operationQuantity.compareTo(remainingVolume) >= 0) {
            infomationDO.setUseEndFlag(1);
            // 新增用完标记记录
            ArrayList<MaterialUseEndReuseDetailSaveReqVO> useEndReuseDetailSaveReqVOS = new ArrayList<>();
            useEndReuseDetailSaveReqVOS.add(new MaterialUseEndReuseDetailSaveReqVO().setInfomationId(infomationId));
            materialUseEndReuseService.createMaterialUseEndReuse(new MaterialUseEndReuseSaveReqVO()
                    .setBusinessType(MaterialNormalOperationType.used_mark.getName())
                    .setRemark("物料已用完，自动标记")
                    .setDetailList(useEndReuseDetailSaveReqVOS));
        }
        materialInfomationService.updateById(infomationDO);

        // 返回
        return BeanUtils.toBean(recordDO, MaterialUseRecordRespVO.class);
    }

    @Override
    public void updateMaterialUseRecord(MaterialUseRecordSaveReqVO updateReqVO) {
        // 校验存在
        validateMaterialUseRecordExists(updateReqVO.getId());
        // 更新
        MaterialUseRecordDO updateObj = BeanUtils.toBean(updateReqVO, MaterialUseRecordDO.class);
        materialUseRecordMapper.updateById(updateObj);
    }

    @Transactional
    @Override
    public void deleteMaterialUseRecord(Long id) {
        MaterialUseRecordDO recordDO = materialUseRecordMapper.selectById(id);
        if (recordDO == null) throw exception(MATERIAL_USE_RECORD_NOT_EXISTS);
        // 删除
        materialUseRecordMapper.deleteById(id);
        // 恢复物料余量
        MaterialInfomationDO infomationDO = materialInfomationService.getMaterialInfomation(recordDO.getInfomationId());
        infomationDO.setRemainingVolume(infomationDO.getRemainingVolume().add(recordDO.getOperationQuantity()));
        materialInfomationService.updateById(infomationDO);
    }

    @Transactional
    @Override
        public void deleteMaterialUseRecordListByIds(List<Long> ids) {
        List<MaterialUseRecordDO> recordDOS = materialUseRecordMapper.selectByIds(ids);
        if (CollUtil.isEmpty(recordDOS) || recordDOS.size() != ids.size()) throw exception(MATERIAL_USE_RECORD_NOT_EXISTS);
        // 恢复物料剩余量
        Set<Long> infIds = recordDOS.stream().map(MaterialUseRecordDO::getInfomationId).collect(Collectors.toSet());
        List<MaterialInfomationDO> infomationDOS = materialInfomationService.getListByIds(new ArrayList<>(infIds));
        if (CollUtil.isEmpty(infomationDOS)) throw new ServiceException(1_032_160_000, "物料实例不存在");
        Map<Long, List<MaterialUseRecordDO>> groupRecordDOS = recordDOS.stream().collect(Collectors.groupingBy(MaterialUseRecordDO::getInfomationId));
        for (MaterialInfomationDO infomationDO : infomationDOS) {
            List<MaterialUseRecordDO> list = groupRecordDOS.get(infomationDO.getId());
            if (CollUtil.isEmpty(list)) continue;
            BigDecimal sum = list.stream().map(MaterialUseRecordDO::getOperationQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
            infomationDO.setRemainingVolume(infomationDO.getRemainingVolume().add(sum));
        }
        materialInfomationService.updateBatch(infomationDOS);
        // 删除
        materialUseRecordMapper.deleteByIds(ids);
        }

    private void validateMaterialUseRecordExists(List<Long> ids) {
        List<MaterialUseRecordDO> list = materialUseRecordMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(MATERIAL_USE_RECORD_NOT_EXISTS);
        }
    }

    private void validateMaterialUseRecordExists(Long id) {
        if (materialUseRecordMapper.selectById(id) == null) {
            throw exception(MATERIAL_USE_RECORD_NOT_EXISTS);
        }
    }

    @Override
    public MaterialUseRecordDO getMaterialUseRecord(Long id) {
        return materialUseRecordMapper.selectById(id);
    }

    @Override
    public PageResult<MaterialUseRecordDO> getMaterialUseRecordPage(MaterialUseRecordPageReqVO pageReqVO) {
        return materialUseRecordMapper.selectPage(pageReqVO);
    }


    @Override
    public PageResult<MaterialUseRecordRespVO> getMaterialUseRecordPageWithInf(MaterialUseRecordPageReqVO pageReqVO) {
        return materialUseRecordMapper.selectPageWithInf(pageReqVO);
    }

    @Override
    public List<MaterialUseRecordRespVO> getUseRecordsWithMaterialInfoByIds(List<Long> ids) {

        return materialUseRecordMapper.selectListWithInf(ids);
    }

    @Override
    public void saveBatchByInfsOutb(MaterialInventoryOutboundDO outboundDO, List<MaterialInfomationDO> hzrdInfs) {
        List<MaterialUseRecordDO> recordDOS = hzrdInfs.stream().map(inf -> {
            MaterialUseRecordDO recordDO = new MaterialUseRecordDO();
            recordDO.setInfomationId(inf.getId())
                    .setHazardousMakeFlag(0)
                    .setUserId(outboundDO.getApplyUserId())
                    .setUserName(outboundDO.getApplyUser())
                    .setUseDate(LocalDateTime.now())
                    .setSupervisorId(outboundDO.getSuperviseUserId())
                    .setSupervisorName(outboundDO.getSuperviseUser())
                    .setOperationQuantity(BigDecimal.ZERO)
                    .setBeforeQuantity(inf.getRemainingVolume())
                    .setAfterQuantity(inf.getRemainingVolume());
            return recordDO;
        }).toList();
        materialUseRecordMapper.insertBatch(recordDOS);
    }

    @Override
    public void saveBatch(List<MaterialUseRecordDO> recordDOS) {
        materialUseRecordMapper.insertBatch(recordDOS);
    }

    @Override
    public Boolean superviseReviews(List<Long> ids) {
        List<MaterialUseRecordDO> recordDOS = materialUseRecordMapper.selectByIds(ids);
        if (CollUtil.isEmpty(recordDOS) || recordDOS.size() != ids.size())
            throw new ServiceException(1_032_160_000, "物料使用记录不存在或数量不匹配");
        // 检查数据是否都是当前监督人的数据
        for (MaterialUseRecordDO recordDO : recordDOS) {
            if (!recordDO.getSupervisorId().equals(SecurityFrameworkUtils.getLoginUserId()))
                throw new ServiceException(1_032_160_000, String.format("当前用户不是记录【%s】的监督人", recordDO.getId()));
        }
        this.updateByIds(ids, new MaterialUseRecordDO()
                .setReviewStatus("1").setReviewDate(LocalDateTime.now()));
        return true;
    }

    @Override
    public void updateByIds(List<Long> ids, MaterialUseRecordDO recordDO) {
        materialUseRecordMapper.update(recordDO, Wrappers.lambdaQuery(MaterialUseRecordDO.class)
                .in(MaterialUseRecordDO::getId, ids));
    }

}