package com.hys.app.service.erp.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hys.app.framework.database.WebPage;
import com.hys.app.framework.exception.ServiceException;

import com.hys.app.framework.util.BeanUtils;
import com.hys.app.framework.util.DateUtil;
import com.hys.app.framework.util.PageConvert;
import com.hys.app.framework.util.StringUtil;
import com.hys.app.mapper.erp.StockInventoryMapper;
import com.hys.app.model.erp.dos.*;
import com.hys.app.model.erp.dto.*;
import com.hys.app.model.erp.enums.DamageinventoryEnum;
import com.hys.app.model.erp.enums.NoBusinessTypeEnum;
import com.hys.app.model.erp.enums.SnCodeStatusEnum;
import com.hys.app.model.erp.enums.StockInventoryStatusEnum;
import com.hys.app.model.erp.vo.GoodsVO;
import com.hys.app.model.erp.vo.ProductVO;
import com.hys.app.model.erp.vo.StockInventoryAllowable;
import com.hys.app.model.erp.vo.StockInventoryVO;
import com.hys.app.model.system.dos.AdminUser;
import com.hys.app.service.erp.*;
import com.hys.app.service.system.AdminUserManager;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 库存盘点单业务接口实现
 *
 * @author dmy
 * 2023-12-05
 */
@Service
@AllArgsConstructor
public class StockInventoryManagerImpl extends ServiceImpl<StockInventoryMapper, StockInventory> implements StockInventoryManager {

    private NoGenerateManager noGenerateManager;
    private StockInventoryProductManager stockInventoryProductManager;
    private AdminUserManager adminUserManager;
    private ProductStockManager productStockManager;
    private GoodsManager goodsManager;
    private ProductManager productManager;
    private ItemSnCodeManager itemSnCodeManager;
    private GoodsSnManager goodsSnManager;


    /**
     * 查询库存盘点单分页列表数据
     *
     * @param params 查询参数
     * @return
     */
    @Override
    public WebPage list(StockInventoryParam params) {
        Page<StockInventory> page = page(new Page<>(params.getPageNo(), params.getPageSize()), new LambdaQueryWrapper<StockInventory>()
                .eq(ObjectUtil.isNotNull(params.getWarehouseId()), StockInventory::getWarehouseId, params.getWarehouseId())
                .eq(ObjectUtil.isNotNull(params.getInventoryPersonId()), StockInventory::getInventoryPersonId, params.getWarehouseId())
                .between(ObjectUtil.isNotNull(params.getStartTime()) && ObjectUtil.isNotNull(params.getEndTime()), StockInventory::getInventoryTime, params.getStartTime(), params.getEndTime()));
        return PageConvert.convert(page);
    }

    /**
     * 新增库存盘点单
     *
     * @param stockInventoryDTO 库存盘点单信息
     */
    @Override
    @Transactional(value = "goodsTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void add(StockInventoryDTO stockInventoryDTO) {
        StockInventory copied = BeanUtil.copyProperties(stockInventoryDTO, StockInventory.class);
        //生成库存盘点单编号
        copied.setSn(noGenerateManager.generate(NoBusinessTypeEnum.StockInventory));
        //保存库存盘点单商品信息
        save(copied);
        List<StockInventoryProduct> list = BeanUtils.toBean(stockInventoryDTO.getProductList(), StockInventoryProduct.class);
        list.forEach(item -> item.setInventoryId(copied.getId()));
        this.stockInventoryProductManager.saveBatch(list);
        //如果这个商品有序列号补习不全序列号关系
        for (StockInventoryProductDTO dto : stockInventoryDTO.getProductList()) {
            StockInventoryProduct product = list.stream().filter(item -> item.getProductId().equals(dto.getProductId())).findFirst().get();
            dto.setId(product.getId());
        }
        saveSnCode(copied.getId(), stockInventoryDTO.getProductList());

    }

    /**
     * 编辑库存盘点单
     *
     * @param id                主键ID
     * @param stockInventoryDTO 库存盘点单信息
     */
    @Override
    @Transactional(value = "goodsTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void edit(Long id, StockInventoryDTO stockInventoryDTO) {
        //获取库存盘点单旧数据
        StockInventory stockInventory = this.getById(id);
        if (DamageinventoryEnum.HAS_HANDLE.equals(stockInventory.getStatus())) {
            throw new ServiceException("该盘点单已同步数据,不允许操作");
        }
        stockInventoryProductManager.remove(new LambdaQueryWrapper<StockInventoryProduct>().eq(StockInventoryProduct::getInventoryId, id));
        List<StockInventoryProduct> list = BeanUtils.toBean(stockInventoryDTO.getProductList(), StockInventoryProduct.class);
        list.forEach(item -> item.setInventoryId(id));
        this.stockInventoryProductManager.saveBatch(list);
        //如果这个商品有序列号补习不全序列号关系
        for (StockInventoryProductDTO dto : stockInventoryDTO.getProductList()) {
            StockInventoryProduct product = list.stream().filter(item -> item.getProductId().equals(dto.getProductId())).findFirst().get();
            dto.setId(product.getId());
        }
        itemSnCodeManager.remove(new LambdaQueryWrapper<ItemSnCodeDO>().eq(ItemSnCodeDO::getEntryOutHouseId, id));
        saveSnCode(id, stockInventoryDTO.getProductList());
    }


    private void saveSnCode(Long stockInventoryId, List<StockInventoryProductDTO> productList) {
        List<ItemSnCodeDO> itemSnCodeDOS = new ArrayList<>();
        //如果有序列号，保存序列号和入库单的关系，更爱序列号的状态
        List<String> arrayList = new ArrayList<>();
        for (StockInventoryProductDTO aDo : productList) {
            ProductDO byId = productManager.getById(aDo.getProductId());
            if (byId.getHaveSn() && CollUtil.isEmpty(aDo.getSnCodeList())) {
                throw new ServiceException("有序列号的商品序列号不能为空");
            }
            if (CollUtil.isNotEmpty(aDo.getSnCodeList())) {
                if (aDo.getInventoryNum() != aDo.getSnCodeList().size()) {
                    throw new ServiceException("序列号个数和盘点个数不等");
                }
                arrayList.addAll(aDo.getSnCodeList().stream().map(GoodsSnDO::getSnCode).collect(Collectors.toList()));
                aDo.getSnCodeList().forEach(item -> {
                    ItemSnCodeDO itemSnCodeDO = new ItemSnCodeDO();
                    itemSnCodeDO.setItemId(aDo.getId());
                    itemSnCodeDO.setEntryOutHouseId(stockInventoryId);
                    itemSnCodeDO.setType("inventory");
                    itemSnCodeDO.setSnCodeId(item.getId());
                    itemSnCodeDOS.add(itemSnCodeDO);
                });
            }
        }
        if (CollUtil.isNotEmpty(arrayList)) {
            long count = goodsSnManager.count(new LambdaQueryWrapper<GoodsSnDO>().in(GoodsSnDO::getSnCode, arrayList));
            if (count != arrayList.size()) {
                throw new ServiceException("部分序列号不存在");
            }
            //保存序列号和子表的关系
            itemSnCodeManager.saveBatch(itemSnCodeDOS);
        }
    }


    @Override
    @Transactional
    public void syncStock(Long id) {
        StockInventory stockInventory = getById(id);
        stockInventory.setStatus(DamageinventoryEnum.HAS_HANDLE);
        updateById(stockInventory);
        List<StockInventoryProduct> goodsList = this.stockInventoryProductManager.list(id);
        List<Long> collect = goodsList.stream().map(StockInventoryProduct::getProductId).collect(Collectors.toList());
        List<ProductStockDO> list = productStockManager.list(new LambdaQueryWrapper<ProductStockDO>()
                .eq(ProductStockDO::getWarehouseId, stockInventory.getWarehouseId())
                .in(ProductStockDO::getProductId, collect));
        List<StockDTO> listStock = new ArrayList<>();
        for (ProductStockDO aDo : list) {
            StockDTO dto = new StockDTO();
            StockInventoryProduct product = goodsList.stream().filter(item -> item.getProductId().equals(aDo.getProductId())).findAny().get();
            dto.setId(aDo.getId());
            dto.setNum(product.getInventoryNum());
            listStock.add(dto);
        }
        productStockManager.updateActualStock(listStock, "inventory");
        //更新序列号的关系
        GoodsSnDO snDO = new GoodsSnDO();
        snDO.setEntryHouseId(null);
        snDO.setInventoryId(null);
        snDO.setOutHouseId(null);
        snDO.setWarehouseId(null);
        snDO.setStatus(SnCodeStatusEnum.NOT_USED);
        goodsSnManager.update(snDO, new LambdaQueryWrapper<GoodsSnDO>()
                .eq(GoodsSnDO::getWarehouseId, stockInventory.getWarehouseId())
                .in(GoodsSnDO::getProductId, goodsList.stream().map(StockInventoryProduct::getProductId).collect(Collectors.toList())));
        //找到这个订单关联的所有序列号
        List<ItemSnCodeDO> listed = itemSnCodeManager.list(new LambdaQueryWrapper<ItemSnCodeDO>()
                .eq(ItemSnCodeDO::getType, "inventory").eq(ItemSnCodeDO::getEntryOutHouseId, id));
        if (CollUtil.isNotEmpty(listed)) {
            List<GoodsSnDO> goodsSnDOS = goodsSnManager.list(new LambdaQueryWrapper<GoodsSnDO>().in(GoodsSnDO::getId, listed.stream().map(ItemSnCodeDO::getSnCodeId).collect(Collectors.toList())));
            //更爱序列号状态
            for (GoodsSnDO aDo : goodsSnDOS) {
                aDo.setStatus(SnCodeStatusEnum.IN_WAREHOUSE);
                aDo.setInventoryId(id);
                aDo.setWarehouseId(stockInventory.getWarehouseId());
                aDo.setInventoryId(id);
            }
            goodsSnManager.updateBatchById(goodsSnDOS);
        }
    }


    /**
     * 获取库存盘点单详情
     *
     * @param id 主键ID
     * @return
     */
    @Override
    public StockInventoryVO getDetail(Long id) {
        StockInventory stockInventory = this.getById(id);
        StockInventoryVO stockInventoryVO = BeanUtil.copyProperties(stockInventory, StockInventoryVO.class);
        //获取库存盘点单商品信息
        List<StockInventoryProduct> goodsList = this.stockInventoryProductManager.list(id);
        List<Long> collect = goodsList.stream().map(StockInventoryProduct::getProductId).collect(Collectors.toList());
        List<ProductStockDO> page = productStockManager.list(new LambdaQueryWrapper<ProductStockDO>()
                .eq(ProductStockDO::getWarehouseId, stockInventory.getWarehouseId())
                .in(ProductStockDO::getProductId, collect));
        List<StockInventoryProductDTO> beanList = BeanUtils.toBean(goodsList, StockInventoryProductDTO.class);
        for (StockInventoryProductDTO dto : beanList) {
            ProductStockDO aDo = page.stream().filter(item -> item.getProductId().equals(dto.getProductId())).findFirst().get();
            ProductVO productVO = BeanUtil.copyProperties(productManager.getById(dto.getProductId()), ProductVO.class);
            dto.setProductVO(productVO);
            GoodsVO goodsVO = BeanUtil.copyProperties(goodsManager.getById(productVO.getGoodsId()), GoodsVO.class);
            dto.setUsableStock(aDo.getUsableStock());
            dto.setActualStock(aDo.getActualStock());
            List<ItemSnCodeDO> list2 = itemSnCodeManager.list(new LambdaQueryWrapper<ItemSnCodeDO>().eq(ItemSnCodeDO::getItemId, dto.getId()));
            if (CollUtil.isNotEmpty(list2)) {
                List<Long> collected = list2.stream().map(ItemSnCodeDO::getSnCodeId).collect(Collectors.toList());
                dto.setSnCodeList(goodsSnManager.list(new LambdaQueryWrapper<GoodsSnDO>().in(GoodsSnDO::getId, collected)));
            }
            dto.setGoodsVO(goodsVO);
        }
        stockInventoryVO.setProductList(beanList);
        return stockInventoryVO;
    }


    /**
     * 删除库存盘点单
     *
     * @param id 库存盘点单主键ID集合
     */
    @Override
    @Transactional(value = "goodsTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void delete(Long id) {
        StockInventory inventory = getById(id);
        if (DamageinventoryEnum.HAS_HANDLE.equals(inventory.getStatus())) {
            throw new ServiceException("该盘点单已同步数据，不允许操作");
        }
        removeById(id);
        stockInventoryProductManager.remove(new LambdaQueryWrapper<StockInventoryProduct>().eq(StockInventoryProduct::getInventoryId, id));
        itemSnCodeManager.remove(new LambdaQueryWrapper<ItemSnCodeDO>().eq(ItemSnCodeDO::getEntryOutHouseId, id));
    }



    /**
     * 库存盘点单提交审核
     *
     * @param ids 库存盘点单主键ID集合
     */
    @Override
    @Transactional(value = "goodsTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void submit(List<Long> ids) {
        List<StockInventory> inventories = this.lambdaQuery()
                .in(StockInventory::getId, ids)
                .list();
        if (inventories == null || inventories.size() == 0) {
            throw new ServiceException("要提交审核的库存盘点单不存在");
        }
        //验证库存盘点单状态是否可以提交审核
        for (StockInventory inventory : inventories) {
            if (!StockInventoryStatusEnum.NEW.name().equals(inventory.getStatus())) {
                throw new ServiceException("只有状态为未提交的库存盘点单才可进行提交审核操作");
            }
        }
        //将库存盘点单状态修改为已提交（待审核）
        this.lambdaUpdate()
                .set(StockInventory::getStatus, StockInventoryStatusEnum.WAIT.name())
                .in(StockInventory::getId, ids)
                .update();
    }

    /**
     * 库存盘点单撤销提交审核
     *
     * @param ids 库存盘点单主键ID集合
     */
    @Override
    @Transactional(value = "goodsTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void cancel(List<Long> ids) {
        List<StockInventory> inventories = this.lambdaQuery()
                .in(StockInventory::getId, ids)
                .list();
        if (inventories == null || inventories.size() == 0) {
            throw new ServiceException("要撤销提交审核的库存盘点单不存在");
        }
        //验证库存盘点单状态是否可以撤销提交
        for (StockInventory inventory : inventories) {
            if (!StockInventoryStatusEnum.WAIT.name().equals(inventory.getStatus())) {
                throw new ServiceException("只有状态为已提交的库存盘点单才可进行撤销操作");
            }
        }
        //将库存盘点单状态修改为未提交
        this.lambdaUpdate()
                .set(StockInventory::getStatus, StockInventoryStatusEnum.NEW.name())
                .in(StockInventory::getId, ids)
                .update();
    }

    /**
     * 审核库存盘点单
     *
     * @param ids          库存盘点单ID集合
     * @param status       审核状态 PASS：审核通过，REJECT：审核驳回
     * @param rejectReason 驳回原因
     */
    @Override
    @Transactional(value = "goodsTransactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void audit(List<Long> ids, String status, String rejectReason) {
        List<StockInventory> inventories = this.lambdaQuery()
                .in(StockInventory::getId, ids)
                .list();
        if (inventories == null || inventories.size() == 0) {
            throw new ServiceException("要进行审核的库存盘点单不存在");
        }
        if (!StockInventoryStatusEnum.PASS.name().equals(status)
                && !StockInventoryStatusEnum.REJECT.name().equals(status)) {
            throw new ServiceException("审核状态不正确");
        }
        if (StockInventoryStatusEnum.REJECT.name().equals(status)) {
            if (StringUtil.isEmpty(rejectReason)) {
                throw new ServiceException("请填写审核驳回原因");
            }
            if (rejectReason.length() > 200) {
                throw new ServiceException("驳回原因不能超过200个字符");
            }
        }
        //获取当前登录管理员
        AdminUser adminUser = this.adminUserManager.getCurrUser();
        //循环审核库存盘点单
        for (StockInventory inventory : inventories) {
            if (!StockInventoryStatusEnum.WAIT.name().equals(inventory.getStatus())) {
                throw new ServiceException("只有状态为待审核的库存盘点单才可以进行审核操作");
            }
        }
        //修改库存盘点单状态
        this.lambdaUpdate()
                .set(StockInventory::getStatus, status)
                .set(StringUtil.notEmpty(rejectReason), StockInventory::getRejectReason, rejectReason)
                .set(StockInventory::getAuditTime, DateUtil.getDateline())
                .set(StockInventory::getAuditPersonId, adminUser.getId())
                .set(StockInventory::getAuditPersonName, adminUser.getRealName())
                .in(StockInventory::getId, ids)
                .update();
    }


}
