package com.fjhx.wms.service.stock.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.common.constant.SourceConstant;
import com.fjhx.common.utils.Assert;
import com.fjhx.item.entity.product.po.ProductInfo;
import com.fjhx.item.service.product.ProductInfoService;
import com.fjhx.wms.entity.arrival.po.ArrivalDetailPo;
import com.fjhx.wms.entity.arrival.po.ArrivalStockRecords;
import com.fjhx.wms.entity.arrival.po.ArrivalStockRecordsDetails;
import com.fjhx.wms.entity.purchase.po.PurchaseDetailPo;
import com.fjhx.wms.entity.stock.dto.StockJournalDetailsDto;
import com.fjhx.wms.entity.stock.dto.StockWaitDetailsDto;
import com.fjhx.wms.entity.stock.dto.StockWaitDto;
import com.fjhx.wms.entity.stock.dto.StockWaitSelectDto;
import com.fjhx.wms.entity.stock.emums.JournalType;
import com.fjhx.wms.entity.stock.emums.StockWaitType;
import com.fjhx.wms.entity.stock.po.*;
import com.fjhx.wms.entity.stock.vo.StockWaitDetailsVo;
import com.fjhx.wms.entity.stock.vo.StockWaitVo;
import com.fjhx.wms.entity.warehouse.po.Warehouse;
import com.fjhx.wms.mapper.stock.StockWaitMapper;
import com.fjhx.wms.service.MyPurchaseService;
import com.fjhx.wms.service.WmsService;
import com.fjhx.wms.service.arrival.ArrivalStockRecordsDetailsService;
import com.fjhx.wms.service.arrival.ArrivalStockRecordsService;
import com.fjhx.wms.service.stock.*;
import com.fjhx.wms.service.warehouse.WarehouseService;
import com.fjhx.wms.utils.CodeEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.wrapper.IWrapper;
import com.ruoyi.system.utils.UserUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * <p>
 * 待出入库 服务实现类
 * </p>
 *
 * @author
 * @since 2023-03-22
 */
@DS(SourceConstant.WMS)
@Service
public class StockWaitServiceImpl extends ServiceImpl<StockWaitMapper, StockWait> implements StockWaitService {
    @Autowired
    private ProductInfoService productInfoService;
    @Autowired
    private StockService stockService;
    @Autowired
    private StockJournalService stockJournalService;
    @Autowired
    private StockJournalDetailsService stockJournalDetailsService;
    @Autowired
    private StockWaitDetailsService stockWaitDetailsService;
    @Autowired
    private WarehouseService warehouseService;
    @Autowired
    private ArrivalStockRecordsService arrivalStockRecordsService;
    @Autowired
    private ArrivalStockRecordsDetailsService arrivalStockRecordsDetailsService;
    @Autowired
    private WmsService wmsService;
    @Autowired
    private StockFrozenService stockFrozenService;
    @Autowired
    private MyPurchaseService myPurchaseService;

    @Override
    public Page<StockWaitVo> getPage(StockWaitSelectDto dto) {
        IWrapper<StockWait> wrapper = getWrapper();
        //过滤入库/出库
        wrapper.eq(StockWait::getType, dto.getType());
        wrapper.eq("sw", StockWait::getBusinessType, dto.getBusinessType());
        wrapper.eq(StockWait::getBusinessCode, dto.getKeyword());
        wrapper.orderByDesc("sw", StockWait::getId);
        Page<StockWaitVo> page = this.baseMapper.getPage1(dto.getPage(), wrapper);

        List<StockWaitVo> records = page.getRecords();
        // 赋值产品属性
        productInfoService.attributeAssign(records, StockWaitVo::getProductId, (item, productInfo) -> {
            item.setProductCode(productInfo.getCode());
            item.setProductUnit(productInfo.getUnit());
            item.setProductType(productInfo.getType());
            item.setProductName(productInfo.getName());
            item.setProductSpec(productInfo.getSpec());
        });

        return page;
    }

    @Override
    public Page<StockWaitVo> getPageByWdly(StockWaitSelectDto dto) {
        IWrapper<StockWait> wrapper = getWrapper();
        wrapper.orderByDesc("sw", StockWait::getId);
        //过滤入库/出库
        wrapper.eq(StockWait::getType, dto.getType());
        wrapper.eq(StockWait::getBusinessCode, dto.getKeyword());
        wrapper.eq(StockWait::getStatus, dto.getStatus());
        wrapper.eq("sw", StockWait::getBusinessType, dto.getBusinessType());

        //过滤掉待入库待出库已完成数据
        wrapper.ne("sw", StockWait::getStatus, 2);

        Page<StockWaitVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);

        List<StockWaitVo> records = page.getRecords();
        //赋值操作人
        UserUtil.assignmentNickName(records, StockWaitVo::getCreateUser, StockWaitVo::setOperatorName);
        //赋值仓库名称和id
        List<Long> warehouseIds = new ArrayList<>();
        for (StockWaitVo stockWaitVo : records) {
            String victoriatouristJson = stockWaitVo.getVictoriatouristJson();
            if (ObjectUtil.isEmpty(victoriatouristJson)) {
                continue;
            }
            JSONObject json = JSONObject.parseObject(victoriatouristJson);
            if (!json.containsKey("receiptWarehouseId")) {
                continue;
            }
            warehouseIds.add(json.getLong("receiptWarehouseId"));
        }
        if (ObjectUtil.isEmpty(warehouseIds)) {
            return page;
        }
        List<Warehouse> warehouses = warehouseService.listByIds(warehouseIds);
        Map<Long, String> warehousesMap = warehouses.stream().collect(Collectors.toMap(Warehouse::getId, Warehouse::getName));
        for (StockWaitVo stockWaitVo : records) {
            String victoriatouristJson = stockWaitVo.getVictoriatouristJson();
            if (ObjectUtil.isEmpty(victoriatouristJson)) {
                continue;
            }
            JSONObject json = JSONObject.parseObject(victoriatouristJson);
            json.put("receiptWarehouseName", warehousesMap.get(json.getLong("receiptWarehouseId")));
            stockWaitVo.setVictoriatouristJson(JSONObject.toJSONString(json, JSONWriter.Feature.WriteLongAsString));
        }

        return page;
    }

    @Override
    public StockWaitVo detail(Long id) {
        StockWait stockWait = this.getById(id);
        StockWaitVo result = BeanUtil.toBean(stockWait, StockWaitVo.class);

        StockWaitDetails stockWaitDetails = stockWaitDetailsService.getOne(q -> q.eq(StockWaitDetails::getStockWaitId, id));
        ProductInfo byId = productInfoService.getById(stockWaitDetails.getProductId());
        result.setProductName(byId.getName());
        result.setQuantity(stockWaitDetails.getQuantity());
        result.setStockWaitDetailsId(stockWaitDetails.getId());

        //获取明细列表
        List<StockWaitDetails> stockWaitDetailsList = stockWaitDetailsService.list(q -> q.eq(StockWaitDetails::getStockWaitId, id));
        List<StockWaitDetailsVo> stockWaitDetailsVos = BeanUtil.copyToList(stockWaitDetailsList, StockWaitDetailsVo.class);
        //赋值产品信息
        productInfoService.attributeAssign(stockWaitDetailsVos, StockWaitDetailsVo::getProductId, (item, productInfo) -> {
            item.setProductName(productInfo.getName());
        });
        result.setStockWaitDetailsList(stockWaitDetailsVos);

        return result;
    }

    @Override
    public StockWaitVo detailByWdly(Long id) {
        StockWait stockWait = this.getById(id);
        StockWaitVo result = BeanUtil.toBean(stockWait, StockWaitVo.class);
        List<StockWaitDetails> list = stockWaitDetailsService.list(q -> q.eq(StockWaitDetails::getStockWaitId, id));
        List<StockWaitDetailsVo> stockWaitDetailsVos = BeanUtil.copyToList(list, StockWaitDetailsVo.class);
        //根据业务id(到货id)查询采购单明细信息


        //赋值物品信息和采购信息
        List<Long> productIds = stockWaitDetailsVos.stream().map(StockWaitDetails::getProductId).collect(Collectors.toList());
        if (ObjectUtil.isNotEmpty(productIds)) {
            List<ProductInfo> productInfos = productInfoService.listByIds(productIds);
            Map<Long, ProductInfo> productInfoMap = productInfos.stream().collect(Collectors.toMap(ProductInfo::getId, Function.identity()));
            for (StockWaitDetailsVo stockWaitDetails : stockWaitDetailsVos) {
                ProductInfo productInfo = productInfoMap.get(stockWaitDetails.getProductId());
                if (ObjectUtil.isEmpty(productInfo)) {
                    continue;
                }
                stockWaitDetails.setProductName(productInfo.getName());
                stockWaitDetails.setProductCode(productInfo.getCode());
                stockWaitDetails.setProductCustomCode(productInfo.getCustomCode());
            }
        }
        result.setStockWaitDetailsList(stockWaitDetailsVos);
        return result;
    }

    @DSTransactional
    @Override
    public void add(StockWaitDetailsDto stockWaitDetailsDto) {
        //优化
        StockWaitDetails stockWaitDetails = stockWaitDetailsService.getById(stockWaitDetailsDto.getId());
        Assert.notEmpty(stockWaitDetails, "查询不到待出库明细信息");
        StockWaitDto stockWaitDto = BeanUtil.copyProperties(stockWaitDetailsDto, StockWaitDto.class);
        stockWaitDto.setId(stockWaitDetails.getStockWaitId());
        stockWaitDto.setStockWaitDetailsList(Collections.singletonList(BeanUtil.copyProperties(stockWaitDetailsDto, StockWaitDetails.class)));
        this.add1(stockWaitDto);

        //如果是采购退货 修改采购退货状态
        StockWait stockWait = this.getById(stockWaitDto.getId());
        Assert.notEmpty(stockWait, "查询不到待出库信息");
        if (stockWait.getBusinessType() == 4) {
            Integer status = stockWait.getStatus();
            myPurchaseService.updateSalesReturnStatus(stockWaitDetails.getBusinessDetailsId(), status);
        }
    }

    @DSTransactional
    @Override
    public void add1(StockWaitDto stockWaitDto) {
        Assert.notEmpty(stockWaitDto.getId(), "待出入库id不能为空");
        Assert.notEmpty(stockWaitDto.getWarehouseId(), "仓库id不能为空");

        List<Stock> stockList = new ArrayList<>();

        List<StockWaitDetails> stockWaitDetailsDtoList = stockWaitDto.getStockWaitDetailsList();
        Map<Long, StockWaitDetails> stockWaitDetailsMap = stockWaitDetailsDtoList.stream().collect(Collectors.toMap(StockWaitDetails::getId, Function.identity()));

        //更新已出入库数量以及待出入库状态
        stockWaitDetailsService.changeStockWaitStatus(stockWaitDto);
        StockWait stockWait = getById(stockWaitDto.getId());

        String victoriatouristJson = stockWait.getVictoriatouristJson();
        JSONObject json = ObjectUtil.isNotEmpty(victoriatouristJson) ? JSONObject.parseObject(victoriatouristJson) : new JSONObject();
        json.put("receiptWarehouseId", stockWaitDto.getWarehouseId());
        stockWait.setVictoriatouristJson(JSONObject.toJSONString(json, JSONWriter.Feature.WriteLongAsString));
        updateById(stockWait);

        //如果业务类型是 采购到货入库 计算结存单价[结存单价=(结存单价*库存数量+采购单价*入库数量)/(库存数量+入库数量)]
        if (StockWaitType.PURCHASE_ARRIVAL_IN.getDetailType().equals(stockWait.getBusinessType())) {
            //获取待入库明细信息
            List<Long> swdId = stockWaitDetailsDtoList.stream().map(StockWaitDetails::getId).collect(Collectors.toList());
            List<StockWaitDetails> oldStockWaitDetailsList = stockWaitDetailsService.listByIds(swdId);
            Map<Long, StockWaitDetails> oldStockWaitDetailsMap = oldStockWaitDetailsList.stream()
                    .collect(Collectors.toMap(StockWaitDetails::getId, Function.identity()));

            List<Long> productIds = oldStockWaitDetailsList.stream().map(StockWaitDetails::getProductId).collect(Collectors.toList());
            //获取库存消息
            Map<Long, Stock> stockMap = stockService.mapKEntity(Stock::getProductId, q -> q
                    .eq(Stock::getWarehouseId, stockWaitDto.getWarehouseId())
                    .in(Stock::getProductId, productIds));
            //获取到货明细信息
            List<Long> arrivalDetailsIds = oldStockWaitDetailsList.stream().map(StockWaitDetails::getBusinessDetailsId).collect(Collectors.toList());
            List<ArrivalDetailPo> arrivalDetailList = myPurchaseService.getArrivalDetailList(arrivalDetailsIds);
            Assert.notEmpty(arrivalDetailList, "到货明细信息为空");
            Map<Long, ArrivalDetailPo> arrivalDetailPoMap = arrivalDetailList.stream().collect(Collectors.toMap(ArrivalDetailPo::getId, Function.identity()));
            //获取采购明细信息
            List<Long> purchaseDetailIds = arrivalDetailList.stream().map(ArrivalDetailPo::getPurchaseDetailId).collect(Collectors.toList());
            List<PurchaseDetailPo> purchaseDetailList = myPurchaseService.getPurchaseDetailList(purchaseDetailIds);
            Assert.notEmpty(arrivalDetailList, "采购明细信息为空");
            Map<Long, PurchaseDetailPo> purchaseDetailPoMap = purchaseDetailList.stream().collect(Collectors.toMap(PurchaseDetailPo::getId, Function.identity()));

            List<Stock> stockList1 = new ArrayList<>();
            for (StockWaitDetails stockWaitDetails : stockWaitDetailsDtoList) {
                StockWaitDetails oldStockWaitDetails = oldStockWaitDetailsMap.get(stockWaitDetails.getId());
                Assert.notEmpty(oldStockWaitDetails, "查询不到待入库明细信息");
                //获取到货信息
                ArrivalDetailPo arrivalDetailPo = arrivalDetailPoMap.get(oldStockWaitDetails.getBusinessDetailsId());
                Assert.notEmpty(arrivalDetailPo, "查询不到到货明细信息");
                //获取采购信息
                PurchaseDetailPo purchaseDetailPo = purchaseDetailPoMap.get(arrivalDetailPo.getPurchaseDetailId());
                Assert.notEmpty(arrivalDetailPo, "查询不到采购明细信息");
                //获取库存信息
                Stock stock = stockMap.get(oldStockWaitDetails.getProductId());
                if (ObjectUtil.isEmpty(stock)) {
                    stock = new Stock();
                    stock.setWarehouseId(stockWaitDto.getWarehouseId());
                    stock.setProductId(oldStockWaitDetails.getProductId());
                    stock.setQuantity(BigDecimal.ZERO);
                    stock.setUnitPrice(BigDecimal.ZERO);
                }
                //计算结存单价 结存单价=(结存单价*库存数量+采购单价*入库数量)/(库存数量+入库数量)
                BigDecimal multiply = stock.getUnitPrice().multiply(stock.getQuantity());//结存单价*库存数量
                BigDecimal multiply1 = purchaseDetailPo.getPrice().multiply(stockWaitDetails.getQuantity());//采购单价*入库数量
                BigDecimal add = multiply.add(multiply1);//(结存单价*库存数量+采购单价*入库数量)
                BigDecimal add1 = stock.getQuantity().add(stockWaitDetails.getQuantity());//库存数量+入库数量
                BigDecimal divide = add.divide(add1, 4, BigDecimal.ROUND_HALF_UP);//(结存单价*库存数量+采购单价*入库数量)/(库存数量+入库数量)
                stock.setUnitPrice(divide);

                stockList1.add(stock);
            }
            stockService.saveOrUpdateBatch(stockList1);
        }

        //操作库存
        List<Long> swdIds = stockWaitDetailsDtoList.stream().map(StockWaitDetails::getId).collect(Collectors.toList());
        Map<Long, StockWaitDetails> oldStockWaitDetailsMap = stockWaitDetailsService.mapKEntity(StockWaitDetails::getId, q -> q.in(StockWaitDetails::getId, swdIds));

        //创建出入库记录
        Integer businessType = stockWait.getBusinessType();
        StockJournal stockJournal = new StockJournal();
        stockJournal.setOpType(stockWait.getType());//根据待入库类型赋值操作类型 1入库 2出库
        stockJournal.setType(getStockJournalType(businessType));
        stockJournal.setCode(stockWait.getType() == 1 ? CodeEnum.SIN_CODE.getCode() : CodeEnum.SOUT_CODE.getCode());
        stockJournal.setWarehouseId(stockWaitDto.getWarehouseId());
        stockJournal.setBusinessId(stockWaitDto.getId());
        //保存出入库记录
        stockJournalService.save(stockJournal);
        List<StockJournalDetails> stockJournalDetailsList = new ArrayList<>();
        for (StockWaitDetails waitDetails : stockWaitDetailsDtoList) {
            StockWaitDetails temp = oldStockWaitDetailsMap.get(waitDetails.getId());
            Stock stock = new Stock();
            stock.setQuantity(waitDetails.getQuantity());
            stock.setProductId(temp.getProductId());
            stockList.add(stock);

            //创建出入库明细
            StockJournalDetails stockJournalDetails = new StockJournalDetailsDto();
            stockJournalDetails.setStockJournalId(stockJournal.getId());
            stockJournalDetails.setProductId(stock.getProductId());
            stockJournalDetails.setQuantity(stock.getQuantity());
            stockJournalDetails.setBusinessDetailsId(waitDetails.getId());
            stockJournalDetailsList.add(stockJournalDetails);

        }

        //操作库存根据待出入库类型来1入库2出库
        stockService.ModifyInventory(stockWait.getType(), stockList, stockWaitDto.getWarehouseId());
        //保存出入库明细
        stockJournalDetailsService.saveBatch(stockJournalDetailsList);

        //如果是采购到货 创建待质检记录
        if (StockWaitType.PURCHASE_ARRIVAL_IN.getDetailType().equals(businessType)) {
            //创建采购到货入库记录
            ArrivalStockRecords arrivalStockRecords = new ArrivalStockRecords();
            arrivalStockRecords.setStockWaitId(stockWait.getId());
            arrivalStockRecords.setWarehouseId(stockWaitDto.getWarehouseId());
            arrivalStockRecords.setQualityStatus(0);
            arrivalStockRecords.setArrivalCode(json.getString("arrivalCode"));//设置到货编码
            arrivalStockRecordsService.save(arrivalStockRecords);
            //创建入库明细
            List<ArrivalStockRecordsDetails> arrivalStockRecordsDetailsList = new ArrayList<>();
            for (StockWaitDetails waitDetails : stockWaitDetailsDtoList) {
                ArrivalStockRecordsDetails arrivalStockRecordsDetails = new ArrivalStockRecordsDetails();
                arrivalStockRecordsDetails.setArrivalStockRecordsId(arrivalStockRecords.getId());
                arrivalStockRecordsDetails.setProductInfoId(waitDetails.getProductId());
                arrivalStockRecordsDetails.setQuantity(waitDetails.getQuantity());
                arrivalStockRecordsDetails.setQualityQuantity(BigDecimal.ZERO);
                arrivalStockRecordsDetailsList.add(arrivalStockRecordsDetails);
            }
            arrivalStockRecordsDetailsService.saveBatch(arrivalStockRecordsDetailsList);
        }

        //销售订单出库
        if (StockWaitType.SALE_ORDER_OUT.getDetailType().equals(businessType)) {
            wmsService.outbounds(stockWaitDto);
        }

        //生产任务待出库
        if (StockWaitType.PRODUCTION_TASK_OUT.getDetailType().equals(businessType)) {
            for (StockWaitDetails stockWaitDetailsDto : stockWaitDetailsDtoList) {
                StockWaitDetails stockWaitDetails = stockWaitDetailsMap.get(stockWaitDetailsDto.getId());
                //减少冻结库存
                StockFrozen stockFrozen = stockFrozenService.getOne(q -> q.eq(StockFrozen::getProductId, stockWaitDetails.getProductId()));
                BigDecimal subtract = stockFrozen.getFrozenQuantity().subtract(stockWaitDetailsDto.getQuantity());
                stockFrozen.setFrozenQuantity(subtract);
                stockFrozenService.updateById(stockFrozen);
            }
        }
    }

    @Override
    public void edit(StockWaitDto stockWaitDto) {
        this.updateById(stockWaitDto);
    }

    @Override
    public void delete(Long id) {
        this.removeById(id);
    }

    /**
     * 根据待出入库业务类型获取对应的出入库记录类型
     */
    @Override
    public Integer getStockJournalType(int businessType) {
        if (StockWaitType.BORDER_OF_LINE_IN.getDetailType().equals(businessType)) {
            //线边回仓库
            return JournalType.BORDER_OF_LINE_IN.getDetailType();
        } else if (StockWaitType.COMPLETION_IN.getDetailType().equals(businessType)) {
            //完工入库
            return JournalType.COMPLETION_IN.getDetailType();
        } else if (StockWaitType.PURCHASE_ARRIVAL_IN.getDetailType().equals(businessType)) {
            //采购到货
            return JournalType.PURCHASE_ARRIVAL_IN.getDetailType();
        } else if (StockWaitType.BACK_OUT.getDetailType().equals(businessType)) {
            //退货出货
            return JournalType.BACK_OUT.getDetailType();
        } else if (StockWaitType.SALE_ORDER_OUT.getDetailType().equals(businessType)) {
            //销售订单出库
            return JournalType.SALES_OUT.getDetailType();
        } else if (StockWaitType.PRODUCTION_TASK_OUT.getDetailType().equals(businessType)) {
            //生产任务待出库
            return JournalType.PRODUCTION_TASK_OUT.getDetailType();
        } else {
            throw new ServiceException("未知待出入库业务类型");
        }
    }

}
