package com.prolog.eis.service.container.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.prolog.eis.constants.base.BasePropertyConstants;
import com.prolog.eis.constants.base.ContainerInfoTypeConstants;
import com.prolog.eis.constants.base.ContainerTaskStatusConstants;
import com.prolog.eis.constants.base.ContainerTaskTypeConstants;
import com.prolog.eis.constants.sys.SysParamConstants;
import com.prolog.eis.dao.container.ContainerInfoDetailMapper;
import com.prolog.eis.dao.container.ContainerInfoMapper;
import com.prolog.eis.dao.container.ContainerStoreChangeMapper;
import com.prolog.eis.dao.container.ContainerTaskMapper;
import com.prolog.eis.dto.item.ItemSyncVO;
import com.prolog.eis.dto.page.PdaStockRespDto;
import com.prolog.eis.dto.rf.DemonstrateTask;
import com.prolog.eis.dto.rf.DemonstrateTaskDto;
import com.prolog.eis.dto.rf.DemonstrateTaskInfo;
import com.prolog.eis.dto.rf.DemonstrateTaskReqDto;
import com.prolog.eis.dto.strategy.ContainerInfoDto;
import com.prolog.eis.dto.wcs.BcrRequestDto;
import com.prolog.eis.enums.base.BaseTypeEnum;
import com.prolog.eis.enums.wms.WmsCarryTaskInCode;
import com.prolog.eis.model.container.ContainerInfo;
import com.prolog.eis.model.container.ContainerInfoDetail;
import com.prolog.eis.model.container.ContainerStoreChange;
import com.prolog.eis.model.container.ContainerTask;
import com.prolog.eis.model.inbound.BizInboundTask;
import com.prolog.eis.model.inbound.BizInboundTaskDetail;
import com.prolog.eis.model.led.LedC;
import com.prolog.eis.service.container.ContainerInfoService;
import com.prolog.eis.service.feign.FeignGoodsService;
import com.prolog.framework.core.exception.PrologException;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.utils.MapUtils;
import com.prolog.upcloud.base.inventory.model.BatchNumberSync;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class ContainerInfoServiceImpl implements ContainerInfoService {

    private final ContainerInfoMapper containerInfoMapper;

    private final ContainerInfoDetailMapper containerInfoDetailMapper;

    private final ContainerTaskMapper containerTaskMapper;

    private final ContainerStoreChangeMapper containerStoreChangeMapper;

    private final FeignGoodsService feignGoodsService;
    private final RedisTemplate redisTemplate;

    @Override
    public List<ContainerInfoDto> getAllStock(int goodsPallet) {
        return containerInfoMapper.getAllStock(goodsPallet);
    }

    @Override
    public List<ContainerInfoDto> getContainerInfoDto(String warehouseArea,String billType) {
        String isSingleTask = String.valueOf(redisTemplate.opsForValue().get(SysParamConstants.SYSPRE_FIX + SysParamConstants.SINGLE_TASK));

        List<ContainerTask> containerTaskList = containerTaskMapper.findByMap(null,ContainerTask.class);
        List<ContainerInfoDto> containerInfoDtoList = containerInfoMapper.getContainerInfoDto(warehouseArea, ContainerInfoTypeConstants.GOODS_PALLET,billType);
        if (Integer.parseInt(isSingleTask) == 0) {
            List<String> collect = containerTaskList.stream().map(ContainerTask::getContainerNo).distinct().collect(Collectors.toList());
            containerInfoDtoList = containerInfoDtoList.stream().filter(containerInfoDto -> !collect.contains(containerInfoDto.getContainerNo())).collect(Collectors.toList());
        } else {
            List<String> full = containerTaskList.stream().filter(s -> s.getContainerType() == ContainerTaskStatusConstants.ONE_FULL_TRAY_TYPE || s.getContainerType() ==  ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE).map(ContainerTask::getContainerNo).distinct().collect(Collectors.toList());
            containerInfoDtoList = containerInfoDtoList.stream().filter(containerInfoDto -> !full.contains(containerInfoDto.getContainerNo())).collect(Collectors.toList());
            Map<String, BigDecimal> collect = containerTaskList.stream().collect(Collectors.toMap(ContainerTask::getContainerSubNo, ContainerTask::getQty, BigDecimal::add));
            for (ContainerInfoDto containerInfoDto : containerInfoDtoList) {
                BigDecimal qty = collect.get(containerInfoDto.getContainerSubNo());
                if (Objects.nonNull(qty)) {
                    containerInfoDto.setQty(containerInfoDto.getQty().subtract(qty));
                }
            }
        }
        return containerInfoDtoList;
    }

    @Override
    public ContainerInfo checkStore(BcrRequestDto bcrRequestDto) {
        Criteria criteria = Criteria.forClass(ContainerInfo.class);
        criteria.restrictionBuilder().eq(ContainerInfo::getContainerNo, bcrRequestDto.getStockId()).build();
        ContainerInfo containerInfo = containerInfoMapper.findByCriteria(criteria).stream().findFirst().orElse(null);
        return containerInfo;
    }

    @Override
    public void createContainerStore(BizInboundTask inboundTask) throws Exception {
        {
            ContainerInfo containerInfo = new ContainerInfo();
            containerInfo.setContainerNo(inboundTask.getContainerNo());
            containerInfo.setContainerType(inboundTask.getContainerType());
            containerInfo.setTaskType(ContainerTaskTypeConstants.INBOUND);
            containerInfo.setIsLock(0);
            containerInfo.setWarehouseArea(inboundTask.getWmsLocationCode());
            containerInfo.setAreaType(Integer.parseInt(WmsCarryTaskInCode.AREA_TYPE.get(inboundTask.getWmsLocationCode()).toString()));
            containerInfo.setCreateTime(LocalDateTime.now());
            containerInfo.setUpdateTime(LocalDateTime.now());
            containerInfoMapper.save(containerInfo);

            List<BizInboundTaskDetail> subList = inboundTask.getSubList();
            List<ContainerInfoDetail> containerInfoDetails = new ArrayList<>();
            if (Objects.isNull(subList)) {
                throw new PrologException("入库单【" + inboundTask.getBillNo() + "】中的入库明细为空，请对比入库汇总表【container_info】对应的入库明细表【container_info_detail】");
            }
            List<BatchNumberSync> batchNumberSync = null;
            List<ItemSyncVO> itemSyncVOS = null;
//            if (inboundTask.getContainerType().equals(ContainerInfoTypeConstants.GOODS_PALLET)) {
//                List<String> lotIds = subList.stream().filter(item -> StrUtil.isNotBlank(item.getLotId())).map(BizInboundTaskDetail::getLotId).distinct().collect(Collectors.toList());
//                List<String> itemIds = subList.stream().filter(item -> StrUtil.isNotBlank(item.getItemId())).map(BizInboundTaskDetail::getItemId).distinct().collect(Collectors.toList());
//                batchNumberSync = inventoryService.getBatchNumberSync(lotIds);
//                itemSyncVOS = feignGoodsService.queryItemMst(itemIds);
//            }
            List<ContainerStoreChange> changeList = new ArrayList<>();
            for (BizInboundTaskDetail bizInboundTaskDetail : subList) {
                ContainerInfoDetail containerInfoDetail = new ContainerInfoDetail();
                containerInfoDetail.setContainerNo(inboundTask.getContainerNo());
                containerInfoDetail.setContainerSubNo(bizInboundTaskDetail.getContainerSubNo());
                containerInfoDetail.setQty(bizInboundTaskDetail.getQty());
                containerInfoDetail.setIsLock(0);
                containerInfoDetail.setLockQty(BigDecimal.ZERO);
                containerInfoDetail.setItemId(bizInboundTaskDetail.getItemId());
                containerInfoDetail.setPackId(bizInboundTaskDetail.getPackId());
                containerInfoDetail.setLotId(bizInboundTaskDetail.getLotId());
                containerInfoDetail.setOwnerId(bizInboundTaskDetail.getOwnerId());
                containerInfoDetail.setCreateTime(LocalDateTime.now());
                containerInfoDetail.setAreaType(containerInfo.getAreaType());
                if (CollectionUtil.isNotEmpty(batchNumberSync)) {
                    BatchNumberSync batchNumber = batchNumberSync.stream().filter(item -> StrUtil.equals(item.getBatchId(), bizInboundTaskDetail.getLotId())).findFirst().orElse(null);
                    if (Objects.isNull(batchNumber)) {
                        log.warn("itemId:{},lotId:{}未查询到批次信息", bizInboundTaskDetail.getItemId(), bizInboundTaskDetail.getLotId());
                    } else {
                        containerInfoDetail.setExpireDate(batchNumber.getBatchExpiredDate());
                        containerInfoDetail.setProdDate(batchNumber.getBatchProductionDate());
                    }
                    ItemSyncVO itemSyncVO = itemSyncVOS.stream().filter(item -> Objects.equals(item.getItemId(), bizInboundTaskDetail.getItemId())).findFirst().orElseThrow(() -> new PrologException("未查询到商品信息"));
                    containerInfoDetail.setMaxVolume(itemSyncVO.getPalletStdQuantity());
                    containerInfoDetail.setSingleVolume(itemSyncVO.getUomList().get(0).getVolume().divide(itemSyncVO.getUomList().get(0).getConversionRate(),2,BigDecimal.ROUND_DOWN));
                    containerInfoDetail.setVolume(containerInfoDetail.getSingleVolume().multiply(bizInboundTaskDetail.getQty()));
                }
                containerInfoDetails.add(containerInfoDetail);

                ContainerStoreChange containerChange = new ContainerStoreChange();
                containerChange.setTaskType(BaseTypeEnum.INBOUND_TYPE.getCode());
                containerChange.setContainerNo(inboundTask.getContainerNo());
                containerChange.setContainerSubNo(inboundTask.getContainerNo());
                containerChange.setSoureQty(BigDecimal.ZERO);
                containerChange.setChangeQty(bizInboundTaskDetail.getQty());
                containerChange.setTargetQty(bizInboundTaskDetail.getQty());
                containerChange.setUpperSystemId(inboundTask.getUpperSystemTaskId());
                containerChange.setItemId(bizInboundTaskDetail.getItemId());
                containerChange.setLotId(bizInboundTaskDetail.getLotId());
                containerChange.setPackId(bizInboundTaskDetail.getPackId());
                containerChange.setRemark("入库");
                containerChange.setOwnerId(bizInboundTaskDetail.getOwnerId());
                containerChange.setCreateTime(LocalDateTime.now());
                changeList.add(containerChange);
            }
            containerInfoDetailMapper.saveBatch(containerInfoDetails);
            containerStoreChangeMapper.saveBatch(changeList);
        }
    }

    @Override
    public ContainerInfo findOneByContainerNo(String containerNo) {
        Criteria criteria = Criteria.forClass(ContainerInfo.class);
        criteria.restrictionBuilder().eq(ContainerInfo::getContainerNo, containerNo).build();
        ContainerInfo containerInfo = containerInfoMapper.findByCriteria(criteria).stream().findFirst()
                .orElseThrow(() -> new PrologException(String.format("没有查询到托盘【%s】的库存信息", containerNo)));
        return containerInfo;
    }


    @Override
    public void clearContainerAllInfo(List<String> contianerList) {
        Criteria criteria = Criteria.forClass(ContainerInfo.class);
        criteria.restrictionBuilder()
                .in(ContainerInfo::getContainerNo, contianerList).build();
        long l = containerInfoMapper.deleteByCriteria(criteria);

        Criteria dtCriteria = Criteria.forClass(ContainerInfoDetail.class);
        dtCriteria.restrictionBuilder()
                .in(ContainerInfoDetail::getContainerNo, contianerList).build();
        long l1 = containerInfoDetailMapper.deleteByCriteria(dtCriteria);
    }

    @Override
    public long updateByMap(Map<String, Object> params, Criteria criteria) {
        return containerInfoMapper.updateMapByCriteria(params, criteria);
    }

    @Override
    public LedC findContainerInfo(String stockId) {

        return containerInfoMapper.findContainerInfo(stockId);
    }

    @Override
    public void updateTaskType(Integer id, Integer taskType) {
        containerInfoMapper.updateMapById(id, MapUtils.put("taskType", taskType).put("updateTime", LocalDateTime.now()).getMap(), ContainerInfo.class);
    }

    @Override
    public List<ContainerInfoDto> getAssignContainerStore(String warehouseArea,String billType) {
        List<ContainerTask> containerTaskList = containerTaskMapper.findByMap(null,ContainerTask.class);
        List<ContainerInfoDto> containerInfoDtoList = containerInfoMapper.getContainerInfoDto(warehouseArea, ContainerInfoTypeConstants.GOODS_PALLET,billType);
        List<String> collect = containerTaskList.stream().map(ContainerTask::getContainerNo).distinct().collect(Collectors.toList());
        containerInfoDtoList = containerInfoDtoList.stream().filter(containerInfoDto -> !collect.contains(containerInfoDto.getContainerNo())).collect(Collectors.toList());
        return containerInfoDtoList;
    }

    /**
     * @param warehouseArea
     * @return
     */
    @Override
    public List<ContainerInfoDto> getPartStrategyContainerInfo(String warehouseArea,String billType) {
        Criteria criteria = new Criteria(ContainerTask.class);
        criteria.restrictionBuilder().eq(ContainerTask::getWmsLocationCode, warehouseArea).build();
        List<ContainerTask> containerTaskList =  containerTaskMapper.findByCriteria(criteria);
        List<ContainerInfoDto> containerInfoDtoList = containerInfoMapper.getContainerInfoDto(warehouseArea, ContainerInfoTypeConstants.GOODS_PALLET,billType);
        List<String> full = containerTaskList.stream().filter(s -> s.getContainerType() == ContainerTaskStatusConstants.ONE_FULL_TRAY_TYPE || s.getContainerType() == ContainerTaskStatusConstants.MORE_FULL_TRAY_TYPE).map(ContainerTask::getContainerNo).distinct().collect(Collectors.toList());
        List<String> arrContainer = containerTaskList.stream().filter(s -> s.getStatus() >= 2).map(ContainerTask::getContainerNo).distinct().collect(Collectors.toList());
        containerInfoDtoList = containerInfoDtoList.stream().filter(containerInfoDto -> !full.contains(containerInfoDto.getContainerNo()) && !arrContainer.contains(containerInfoDto.getContainerNo())).collect(Collectors.toList());
        Map<String, BigDecimal> collect = containerTaskList.stream().collect(Collectors.toMap(ContainerTask::getContainerSubNo, ContainerTask::getQty, BigDecimal::add));
        for (ContainerInfoDto containerInfoDto : containerInfoDtoList) {
            BigDecimal qty = collect.get(containerInfoDto.getContainerSubNo());
            if (Objects.nonNull(qty)) {
                containerInfoDto.setQty(containerInfoDto.getQty().subtract(qty));
            }
        }
        return containerInfoDtoList;
    }

    @Override
    public List<ContainerInfoDto> getEmptyContainerInfoDto(String warehouseArea,String billType) {
        List<ContainerTask> containerTaskList = containerTaskMapper.findByMap(null,ContainerTask.class);
        List<ContainerInfoDto> containerInfoDtoList = containerInfoMapper.getContainerInfoDto(warehouseArea, ContainerInfoTypeConstants.EMPTY_PALLET_PILE,billType);
        List<String> collect = containerTaskList.stream().map(ContainerTask::getContainerNo).distinct().collect(Collectors.toList());
        containerInfoDtoList = containerInfoDtoList.stream().filter(containerInfoDto -> !collect.contains(containerInfoDto.getContainerNo())).collect(Collectors.toList());
        return containerInfoDtoList;
    }

    @Override
    public List<ContainerInfo> getEmptyContainerInfo(String warehouseArea) {
        Criteria criteria = new Criteria(ContainerInfo.class);
        criteria.restrictionBuilder().eq(ContainerInfo::getWarehouseArea, warehouseArea)
                .eq(ContainerInfo::getContainerType, ContainerInfoTypeConstants.EMPTY_PALLET)
                .eq(ContainerInfo::getTaskType, ContainerTaskTypeConstants.IDLE).build();
        return containerInfoMapper.findByCriteria(criteria);
    }

    @Override
    public List<PdaStockRespDto> stockList(String containerNo) throws Exception {
        List<PdaStockRespDto> pdaStockRespDtos = containerInfoMapper.pdaStockList(containerNo);
        for (PdaStockRespDto pdaStockRespDto : pdaStockRespDtos) {
            final ItemSyncVO itemInfo = feignGoodsService.getItemInfo(String.valueOf(pdaStockRespDto.getItemId()));
            pdaStockRespDto.setItemCode(itemInfo.getItemCode());
            pdaStockRespDto.setPackSize(itemInfo.getUomList().get(0).getConversionRate());
            pdaStockRespDto.setItemName(itemInfo.getItemName());
        }
        return pdaStockRespDtos;
    }

    @Override
    public long updateMapByCriteria(Map<String, Object> map, Criteria criteriaDetail) {
        return containerInfoMapper.updateMapByCriteria(map, criteriaDetail);
    }

    @Override
    public List<ContainerInfo> findByMap(Map<String, Object> map) {
        return containerInfoMapper.findByMap(map, ContainerInfo.class);
    }

    @Override
    public void save(ContainerInfo containerInfo) {
        this.containerInfoMapper.save(containerInfo);
    }

    @Override
    @Transactional
    public void createEmptyStore(String containerNo, String warehouseArea) {
        ContainerInfo containerInfo = new ContainerInfo();
        containerInfo.setContainerNo(containerNo);
        containerInfo.setContainerType(ContainerInfoTypeConstants.EMPTY_PALLET_PILE);
        containerInfo.setTaskType(BaseTypeEnum.INBOUND_TYPE.getCode());
        containerInfo.setIsLock(0);
        containerInfo.setWarehouseArea(warehouseArea);
        containerInfo.setAreaType(Integer.parseInt(WmsCarryTaskInCode.AREA_TYPE.getOrDefault(warehouseArea,0).toString()));
        containerInfo.setCreateTime(LocalDateTime.now());
        containerInfo.setUpdateTime(LocalDateTime.now());
        this.save(containerInfo);

        ContainerInfoDetail containerInfoDetail = new ContainerInfoDetail();
        containerInfoDetail.setContainerNo(containerNo);
        containerInfoDetail.setContainerSubNo(containerNo);
        containerInfoDetail.setItemId(BasePropertyConstants.EMPTY_PALLET_PILE);
        containerInfoDetail.setLotId(BasePropertyConstants.EMPTY_PALLET_PILE);
        containerInfoDetail.setOwnerId(BasePropertyConstants.EMPTY_PALLET_PILE);
        containerInfoDetail.setPackId(BasePropertyConstants.EMPTY_PALLET_PILE);
        containerInfoDetail.setQty(BigDecimal.ONE);
        containerInfoDetail.setIsLock(0);
        containerInfoDetail.setAreaType(containerInfo.getAreaType());
        containerInfoDetail.setCreateTime(LocalDateTime.now());
        containerInfoDetailMapper.save(containerInfoDetail);
    }

    @Override
    public List<DemonstrateTask> findStockList(DemonstrateTaskDto demonstrateTaskDto) {
        return containerInfoMapper.findStockList(demonstrateTaskDto);
    }

    @Override
    public void updateMapById(Integer id, Map<String, Object> params) {
        containerInfoMapper.updateMapById(id,params,ContainerInfo.class);
    }

    @Override
    public DemonstrateTaskInfo findDemonstrateTaskInfo(DemonstrateTaskReqDto demonstrateTaskReqDto) {
        return containerInfoMapper.findDemonstrateTaskInfo(demonstrateTaskReqDto);
    }

    @Override
    public void update(ContainerInfo containerInfo) {
        containerInfoMapper.update(containerInfo);
    }

    @Override
    public void deleteById(Integer id) {
        containerInfoMapper.deleteById(id, ContainerInfo.class);
    }
}
