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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
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.hys.app.converter.erp.StockTransferConverter;
import com.hys.app.converter.erp.StockTransferProductConverter;
import com.hys.app.framework.database.WebPage;
import com.hys.app.framework.database.mybatisplus.base.BaseServiceImpl;
import com.hys.app.framework.exception.ServiceException;
import com.hys.app.framework.rabbitmq.MessageSender;
import com.hys.app.framework.rabbitmq.MqMessage;
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.mapper.erp.StockTransferMapper;
import com.hys.app.mapper.erp.WarehouseOutMapper;
import com.hys.app.model.base.rabbitmq.AmqpExchange;
import com.hys.app.model.erp.dos.*;
import com.hys.app.model.erp.dto.*;
import com.hys.app.model.erp.enums.*;
import com.hys.app.model.erp.vo.*;
import com.hys.app.model.system.dos.AdminUser;
import com.hys.app.service.erp.*;
import com.hys.app.service.system.AdminUserManager;
import com.hys.app.service.system.DeptManager;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 库存调拨业务层实现
 *
 * @author 张崧
 * @since 2023-12-12 11:59:06
 */
@Service
@AllArgsConstructor
public class StockTransferManagerImpl extends BaseServiceImpl<StockTransferMapper, StockTransferDO> implements StockTransferManager {

    private StockTransferConverter converter;
    private StockTransferProductConverter itemConverter;
    private NoGenerateManager noGenerateManager;
    private StockTransferProductManager stockTransferProductManager;
    private ProductStockManager productStockManager;
    private StockTransferMapper stockTransferMapper;
    private MessageSender messageSender;
    private AdminUserManager adminUserManager;
    private WarehouseEntryBatchManager batchManager;
    private GoodsManager goodsManager;
    private ProductManager productManager;
    private WarehouseOutMapper warehouseOutMapper;
    private WarehouseOutItemManager warehouseOutItemManager;
    private WarehouseEntryManager warehouseEntryManager;
    private WarehouseEntryProductManager warehouseEntryProductManager;
    private LockedStockDetailManager lockedStockDetailManager;

    @Override
    public WebPage<StockTransferVO> list(StockTransferQueryParams queryParams) {
        WebPage<StockTransferDO> webPage = baseMapper.selectPage(queryParams);
        return converter.convert(webPage);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(StockTransferDTO stockTransferDTO) {
        stockTransferDTO.isSameWarehouse();
        // 保存调拨单
        StockTransferDO aDo = BeanUtil.copyProperties(stockTransferDTO, StockTransferDO.class);
        aDo.setSn(noGenerateManager.generate(NoBusinessTypeEnum.StockTransfer, 78L));
        save(aDo);
        // 保存调拨单明细
        List<StockTransferProductDO> doList = BeanUtils.toBean(stockTransferDTO.getProductList(), StockTransferProductDO.class);
        doList.forEach(item -> item.setStockTransferId(aDo.getId()));
        stockTransferProductManager.saveBatch(doList);


        List<Long> collect = stockTransferDTO.getProductList().stream().map(StockTransferProductDTO::getProductId).collect(Collectors.toList());
        //需要调出的仓库商品库存
        List<ProductStockDO> listOut = productStockManager.list(new LambdaQueryWrapper<ProductStockDO>()
                .eq(ProductStockDO::getWarehouseId, stockTransferDTO.getOutWarehouseId())
                .in(ProductStockDO::getProductId, collect));
        List<StockDTO> arrayList = new ArrayList<>();
        //保存凍結库存明细
        List<LockedStockDetailDO> detailDOS = new ArrayList<>();
        for (ProductStockDO aDoOut : listOut) {
            StockTransferProductDO productDTO = doList.stream().filter(item -> item.getProductId().equals(aDoOut.getProductId())).findFirst().get();
            if (aDoOut.getActualStock() < productDTO.getNum()) {
                throw new ServiceException(productDTO.getNum() + "的库存数量少于调拨出的库存数量");
            }
            StockDTO dto = new StockDTO();
            dto.setId(aDoOut.getId());
            dto.setNum(productDTO.getNum());
            arrayList.add(dto);
            LockedStockDetailDO detailDO = new LockedStockDetailDO();
            detailDO.setProductStockId(aDoOut.getId());
            detailDO.setOrderId(aDo.getId());
            detailDO.setOrderSn(aDo.getSn());
            detailDO.setOrderItemId(productDTO.getId());
            detailDO.setOrderType(DetailOrderTypeEnum.INVENTORY);
            detailDO.setLockedNum(productDTO.getNum());
            detailDO.setLockedTime(new Date());
            detailDOS.add(detailDO);
        }
        lockedStockDetailManager.saveBatch(detailDOS);
        productStockManager.updateLockedStock(arrayList);


        //生成出库单
        WarehouseOutDO outDO = new WarehouseOutDO();
        outDO.setType(WarehouseOutTypeEnum.TRANSFER_OUT);
        outDO.setOrderIdList(aDo.getId());
        outDO.setWarehouseId(aDo.getOutWarehouseId());
        outDO.setNotes("调拨出生成的出货单");
        outDO.setSn(noGenerateManager.generate(NoBusinessTypeEnum.WarehouseOut, 66L));
        warehouseOutMapper.insert(outDO);
        //保存出库单明细
        List<WarehouseOutItemDO> beanList = BeanUtils.toBean(doList, WarehouseOutItemDO.class);
        beanList.forEach(item -> {
            item.setOrderItemId(item.getId());
            item.setOrderId(aDo.getId());
            item.setWarehouseOutId(outDO.getId());
            Integer num = doList.stream().filter(it -> it.getProductId().equals(item.getProductId())).findFirst().get().getNum();
            item.setOutNum(num);
            item.setId(null);
        });
        warehouseOutItemManager.saveBatch(beanList);
        //生成入库单
        WarehouseEntryDO warehouseEntryDO = new WarehouseEntryDO();
        warehouseEntryDO.setWarehouseId(stockTransferDTO.getInWarehouseId());
        warehouseEntryDO.setOrderId(aDo.getId());
        warehouseEntryDO.setType(WarehouseEntyrTypeEnum.TRANSFER_ENTRY);
        warehouseEntryDO.setMemo("调拨单生成的入库单");
        warehouseEntryDO.setSn(noGenerateManager.generate(NoBusinessTypeEnum.WarehouseEntry));
        warehouseEntryManager.save(warehouseEntryDO);
        // 保存入库单商品明细
        List<WarehouseEntryProductDO> productList = BeanUtils.toBean(stockTransferDTO.getProductList(), WarehouseEntryProductDO.class);
        productList.forEach(item -> {
            item.setWarehouseEntryId(warehouseEntryDO.getId());
            Integer num = doList.stream().filter(it -> it.getProductId().equals(item.getProductId())).findFirst().get().getNum();
            item.setNum(num);
        });
        warehouseEntryProductManager.saveBatch(productList);
    }

    @Override
    public StockTransferVO getDetail(Long id) {
        StockTransferVO stockTransferVO = converter.convert(getById(id));
        List<StockTransferProductDO> productList = stockTransferProductManager.listByStockTransferId(id);
        List<StockTransferProductVO> convert = itemConverter.convert(productList);
        convert.forEach(item -> {
            ProductVO productVO = BeanUtil.copyProperties(productManager.getById(item.getProductId()), ProductVO.class);
            item.setProductVO(productVO);
            item.setGoodsVO(BeanUtil.copyProperties(goodsManager.getById(productVO.getGoodsId()), GoodsVO.class));
        });
        stockTransferVO.setProductList(convert);
        return stockTransferVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void withdraw(Long id) {
        StockTransferDO transferDO = getById(id);
        if (TransferTypeEnum.ROLLBACK_HANDLE.equals(transferDO.getStatus())) {
            throw new ServiceException("该数据已经回滚,请勿重复操作");
        }
        transferDO.setStatus(TransferTypeEnum.ROLLBACK_HANDLE);
        updateById(transferDO);
        //库存回滚
        List<StockTransferProductDO> productList = stockTransferProductManager.listByStockTransferId(id);
        List<Long> collect = productList.stream().map(StockTransferProductDO::getProductId).collect(Collectors.toList());
        //需要调出的仓库商品库存
        List<ProductStockDO> listOut = productStockManager.list(new LambdaQueryWrapper<ProductStockDO>()
                .eq(ProductStockDO::getWarehouseId, transferDO.getOutWarehouseId())
                .in(ProductStockDO::getProductId, collect));
        List<ProductStockDO> listIn = productStockManager.list(new LambdaQueryWrapper<ProductStockDO>()
                .eq(ProductStockDO::getWarehouseId, transferDO.getInWarehouseId())
                .in(ProductStockDO::getProductId, collect));

        for (ProductStockDO aDo : listIn) {
            Integer num = productList.stream().filter(item -> item.getProductId().equals(aDo.getProductId())).findFirst().get().getNum();
            if (aDo.getUsableStock() < num) {
                throw new ServiceException(num + "的库存数量少于调拨出的库存数量,不足以支持回滚数量");
            }
            //扣减调拨仓库的库存数量
            aDo.setUsableStock(aDo.getUsableStock() + num);
            aDo.setActualStock(aDo.getActualStock() + num);
            for (ProductStockDO stockDO : listOut) {
                stockDO.setUsableStock(stockDO.getUsableStock() + num);
                stockDO.setActualStock(stockDO.getActualStock() + num);
            }
        }
        listOut.addAll(listIn);
        productStockManager.updateBatchById(listOut);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(StockTransferDTO stockTransferDTO) {
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<Long> ids) {

    }

    @Override
    public void submit(Long id) {
        // 校验
        StockTransferDO oldDO = getById(id);


        lambdaUpdate()
                .set(StockTransferDO::getStatus, StockTransferStatusEnum.Submit)
                .eq(StockTransferDO::getId, id)
                .update();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirm(List<String> ids, Long handleById) {
        List<StockTransferDO> stockTransferList = listByIds(ids);
        for (StockTransferDO stockTransferDO : stockTransferList) {

        }

        AdminUser adminUser = adminUserManager.getModel(handleById);
        if (adminUser == null) {
            throw new ServiceException("经手人不存在");
        }

        // 更新调拨单状态
        lambdaUpdate()
                .set(StockTransferDO::getStatus, StockTransferStatusEnum.Confirm)
                .set(StockTransferDO::getInHandledById, handleById)
                .set(StockTransferDO::getInHandledByName, adminUser.getRealName())
                .in(StockTransferDO::getId, ids)
                .update();

        for (StockTransferDO stockTransferDO : stockTransferList) {
            List<StockTransferProductDO> transferProductList = stockTransferProductManager.listByStockTransferId(stockTransferDO.getId());
            // 减少批次的库存
            List<StockUpdateDTO> stockReduceList = converter.convertStockUpdateList(transferProductList);
            batchManager.updateStock(StockChangeSourceEnum.STOCK_TRANSFER, stockTransferDO.getSn(), stockReduceList);

            // 创建调入仓库的入库批次
            List<WarehouseEntryBatchDO> batchList = converter.combination(stockTransferDO, transferProductList, noGenerateManager);
            batchManager.create(StockChangeSourceEnum.STOCK_TRANSFER, stockTransferDO.getSn(), batchList);
        }

        //审核通过后，需要发送异步消息
        StockStatisticsMessage message = new StockStatisticsMessage();
        message.setStockStatisticsTypeEnum(StockStatisticsTypeEnum.STOCK_TRANSFER);
        message.setStockTransferList(stockTransferList);
        this.messageSender.send(new MqMessage(AmqpExchange.STOCK_STATISTICS, AmqpExchange.STOCK_STATISTICS + "_ROUTING", message));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reject(List<String> ids, Long handleById) {
        List<StockTransferDO> stockTransferList = listByIds(ids);
        for (StockTransferDO stockTransferDO : stockTransferList) {

        }

        AdminUser adminUser = adminUserManager.getModel(handleById);
        if (adminUser == null) {
            throw new ServiceException("经手人不存在");
        }

        // 更新调拨单状态
        lambdaUpdate()
                .set(StockTransferDO::getStatus, StockTransferStatusEnum.NotSubmit)
                .set(StockTransferDO::getInHandledById, handleById)
                .set(StockTransferDO::getInHandledByName, adminUser.getRealName())
                .in(StockTransferDO::getId, ids)
                .update();
    }

    /**
     * 查询库存调拨统计分页列表数据
     *
     * @param params 查询参数
     * @return
     */
    @Override
    public WebPage statistics(StockTransferStatisticsParam params) {
        IPage<StockTransferStatistics> iPage = this.stockTransferMapper.selectStockTransferPage(new Page(params.getPageNo(), params.getPageSize()), params);
        return PageConvert.convert(iPage);
    }

    /**
     * 导出库存调拨统计列表
     *
     * @param response
     * @param params   查询参数
     */
    @Override
    public void export(HttpServletResponse response, StockTransferStatisticsParam params) {
        //查询库存调拨商品列表
        List<StockTransferStatistics> list = this.stockTransferMapper.selectStockTransferList(params);

        ArrayList<Map<String, Object>> rows = CollUtil.newArrayList();
        ExcelWriter writer = ExcelUtil.getWriter(true);
        for (StockTransferStatistics statistics : list) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("调拨单号", statistics.getSn());
            map.put("调拨时间", DateUtil.toString(statistics.getTransferTime(), "yyyy-MM-dd HH:mm:ss"));
            map.put("入库单号", statistics.getWarehouseEntrySn());
            map.put("商品编号", statistics.getProductSn());
            map.put("商品名称", statistics.getProductName());
            map.put("商品类别", statistics.getCategoryName());
            map.put("规格型号", statistics.getProductSpecification());
            map.put("单位", statistics.getProductUnit());
            map.put("调入仓库", statistics.getInWarehouseName());
            map.put("调出仓库", statistics.getOutWarehouseName());
            map.put("调拨数量", statistics.getNum());
            rows.add(map);
        }

        writer.write(rows, true);

        ServletOutputStream out = null;
        try {
            out = response.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=stock_transfer.xlsx");
        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);
    }

    @Override
    public long countByWarehouseId(Long warehouseId) {
        return lambdaQuery()
                .eq(StockTransferDO::getInWarehouseId, warehouseId)
                .or()
                .eq(StockTransferDO::getOutWarehouseId, warehouseId)
                .count();
    }
}

