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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
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.item.entity.product.po.ProductInfo;
import com.fjhx.item.service.product.ProductInfoService;
import com.fjhx.item.util.excel.util.ExcelUtil;
import com.fjhx.tenant.entity.dict.dto.DictTenantDataSelectDto;
import com.fjhx.tenant.entity.dict.vo.DictTenantDataVo;
import com.fjhx.tenant.service.dict.DictTenantDataService;
import com.fjhx.wms.entity.monthly.MonthlyInventoryReportExcel;
import com.fjhx.wms.entity.monthly.dto.MonthlyInventoryReportSelectDto;
import com.fjhx.wms.entity.monthly.po.MonthlyInventoryReport;
import com.fjhx.wms.entity.monthly.vo.MonthlyInventoryReportVo;
import com.fjhx.wms.entity.stock.po.Stock;
import com.fjhx.wms.entity.stock.vo.StockJournalDetailsVo;
import com.fjhx.wms.mapper.monthly.MonthlyInventoryReportMapper;
import com.fjhx.wms.service.monthly.MonthlyInventoryReportService;
import com.fjhx.wms.service.stock.StockService;
import com.fjhx.wms.service.warehouse.WarehouseService;
import com.ruoyi.common.annotation.TenantIgnore;
import com.ruoyi.common.utils.wrapper.IWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 * 月度库存报表 服务实现类
 * </p>
 *
 * @author
 * @since 2023-07-05
 */
@Service
public class MonthlyInventoryReportServiceImpl extends ServiceImpl<MonthlyInventoryReportMapper, MonthlyInventoryReport> implements MonthlyInventoryReportService {
    @Autowired
    private StockService stockService;
    @Autowired
    private ProductInfoService productInfoService;
    @Autowired
    private WarehouseService warehouseService;
    @Autowired
    private DictTenantDataService dictTenantDataService;

    @Override
    public Page<MonthlyInventoryReportVo> getPage(MonthlyInventoryReportSelectDto dto) {
        IWrapper<MonthlyInventoryReport> wrapper = getWrapper();
        wrapper.eq("date_format(mir.daily_report_date,'%Y')", dto.getYear());
        wrapper.eq("date_format(mir.daily_report_date,'%m')", dto.getMonth());
        wrapper.eq(MonthlyInventoryReport::getWarehouseId, dto.getWarehouseId());

        //关键字搜索
        if (ObjectUtil.isNotEmpty(dto.getKeyword())) {
            List<Long> productInfoIds = productInfoService.listObject(ProductInfo::getId, q -> q.like(ProductInfo::getCode, dto.getKeyword()).or().like(ProductInfo::getName, dto.getKeyword()).or().like(ProductInfo::getSpec, dto.getKeyword()));
            productInfoIds.add(null);//插入一个空元素防止为空in条件被忽略
            wrapper.in(MonthlyInventoryReport::getProductId, productInfoIds);
        }

        wrapper.orderByDesc("mir", MonthlyInventoryReport::getId);
        Page<MonthlyInventoryReportVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);
        List<MonthlyInventoryReportVo> records = page.getRecords();
        //赋值产品名称
        productInfoService.attributeAssign(records, MonthlyInventoryReportVo::getProductId, (item, productInfo) -> {
            item.setProductCode(productInfo.getCode());
            item.setProductName(productInfo.getName());
            item.setProductSpec(productInfo.getSpec());
            item.setProductUnit(productInfo.getUnit());
        });
        //赋值仓库名称
        warehouseService.attributeAssign(records, MonthlyInventoryReportVo::getWarehouseId, (item, warehouse) -> {
            item.setWarehouseName(warehouse.getName());
        });

        return page;
    }

    /**
     * 每月1号的0:10:00执行自动生成上月报表
     */
    @Scheduled(cron = "0 10 0 1 * ?")
    @TenantIgnore
    @DSTransactional
    public void generateReport() {
        DynamicDataSourceContextHolder.push(SourceConstant.WMS);
        try {
            List<MonthlyInventoryReport> monthlyInventoryReportList = new ArrayList<>();

            //获取上上月的报表的 结存数量为上月报表的初期数量 并根据租户id分组
            List<MonthlyInventoryReportVo> olDmonthlyInventoryReportLists = baseMapper.getLastMonthMonthlyInventoryReport();
            Map<String, List<MonthlyInventoryReportVo>> olDmonthlyInventoryReportMap = new HashMap<>();
            if (ObjectUtil.isNotEmpty(olDmonthlyInventoryReportLists)) {
                olDmonthlyInventoryReportMap = olDmonthlyInventoryReportLists.stream().collect(Collectors.groupingBy(MonthlyInventoryReportVo::getTenantId));
            }
            //获取上月流水 并根据租户id分组
            List<StockJournalDetailsVo> lastMonthStockJournals = baseMapper.getLastMonthStockJournal();
            Map<String, List<StockJournalDetailsVo>> lastMonthStockJournalMap = new HashMap<>();
            if (ObjectUtil.isNotEmpty(lastMonthStockJournals)) {
                lastMonthStockJournalMap = lastMonthStockJournals.stream().collect(Collectors.groupingBy(StockJournalDetailsVo::getTenantId));
            }

            //获取库存 并根据租户id分组
            List<Stock> stockLists = stockService.list();
            if (ObjectUtil.isEmpty(stockLists)) {
                //如果查询不到一条库存信息直接跳过
                return;
            }
            Map<String, List<Stock>> stockListMap = stockLists.stream().collect(Collectors.groupingBy(Stock::getTenantId));

            //遍历每个租户
            for (Map.Entry<String, List<Stock>> entry : stockListMap.entrySet()) {
                String tenantId = entry.getKey();
                List<Stock> stockList = entry.getValue();
                if (ObjectUtil.isEmpty(stockList)) {
                    //如果租户库存的信息为空直接跳过这个租户
                    continue;
                }

                List<MonthlyInventoryReportVo> olDmonthlyInventoryReportList = olDmonthlyInventoryReportMap.getOrDefault(tenantId, new ArrayList<>());
                List<StockJournalDetailsVo> lastMonthStockJournal = lastMonthStockJournalMap.getOrDefault(tenantId, new ArrayList<>());

                //生成上月报表
                for (Stock stock : stockList) {
                    //计算初期数量
                    List<MonthlyInventoryReport> initialQuantityList = olDmonthlyInventoryReportList.stream().filter(it -> stock.getWarehouseId().equals(it.getWarehouseId())).filter(it -> stock.getProductId().equals(it.getProductId())).collect(Collectors.toList());
                    //计算入库数量
                    List<StockJournalDetailsVo> receiptQuantityList = lastMonthStockJournal.stream().filter(it -> stock.getWarehouseId().equals(it.getToWarehouseId())).filter(it -> stock.getProductId().equals(it.getProductId())).filter(it -> it.getOpType() == 1).collect(Collectors.toList());
                    BigDecimal receiptQuantity = receiptQuantityList.stream().map(StockJournalDetailsVo::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
                    //计算出库数量
                    List<StockJournalDetailsVo> outboundQuantityList = lastMonthStockJournal.stream().filter(it -> stock.getWarehouseId().equals(it.getToWarehouseId())).filter(it -> stock.getProductId().equals(it.getProductId())).filter(it -> it.getOpType() == 2).collect(Collectors.toList());
                    BigDecimal outboundQuantity = outboundQuantityList.stream().map(StockJournalDetailsVo::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);

                    MonthlyInventoryReport monthlyInventoryReport = new MonthlyInventoryReport();
                    monthlyInventoryReport.setWarehouseId(stock.getWarehouseId());
                    monthlyInventoryReport.setProductId(stock.getProductId());
                    monthlyInventoryReport.setInitialQuantity(BigDecimal.ZERO);//初期数量
                    if (ObjectUtil.isNotEmpty(initialQuantityList)) {
                        //初期数量=上上月报表的结存数量
                        monthlyInventoryReport.setInitialQuantity(initialQuantityList.get(0).getBalanceQuantity());
                    }
                    monthlyInventoryReport.setReceiptQuantity(receiptQuantity);//入库数量
                    monthlyInventoryReport.setOutboundQuantity(outboundQuantity);//出库数量
                    monthlyInventoryReport.setBalanceQuantity(BigDecimal.ZERO);//结存数量
                    if (ObjectUtil.isNotEmpty(stock.getQuantity())) {
                        monthlyInventoryReport.setBalanceQuantity(stock.getQuantity());//结存数量
                    }
                    monthlyInventoryReport.setBalanceUnitPrice(BigDecimal.ZERO);//结存单价
                    BigDecimal unitPriceOfBalance = stock.getUnitPrice();
                    if (ObjectUtil.isNotEmpty(unitPriceOfBalance)) {
                        monthlyInventoryReport.setBalanceUnitPrice(unitPriceOfBalance);//结存单价
                    }
                    BigDecimal multiply = monthlyInventoryReport.getBalanceQuantity().multiply(monthlyInventoryReport.getBalanceUnitPrice());
                    monthlyInventoryReport.setBalanceAmount(multiply);//结存金额

                    //赋值上月第一天
                    Calendar cal = Calendar.getInstance();
                    cal.add(Calendar.MONTH, -1);
                    cal.set(Calendar.DAY_OF_MONTH, 1);
                    Date startTime = cal.getTime();//上个月第一天
                    monthlyInventoryReport.setDailyReportDate(startTime);

                    //赋值租户id
                    monthlyInventoryReport.setTenantId(tenantId);
                    monthlyInventoryReportList.add(monthlyInventoryReport);
                }
            }

            this.saveBatch(monthlyInventoryReportList);
        } finally {
            DynamicDataSourceContextHolder.clear();
        }
    }

    @Override
    public void exportExcel(MonthlyInventoryReportSelectDto dto, HttpServletResponse httpServletResponse) {
        IWrapper<MonthlyInventoryReport> wrapper = getWrapper();
        wrapper.eq("date_format(daily_report_date,'%Y')", dto.getYear());
        wrapper.eq("date_format(daily_report_date,'%m')", dto.getMonth());
        wrapper.eq(MonthlyInventoryReport::getWarehouseId, dto.getWarehouseId());

        //关键字搜索
        if (ObjectUtil.isNotEmpty(dto.getKeyword())) {
            List<Long> productInfoIds = productInfoService.listObject(ProductInfo::getId, q -> q.like(ProductInfo::getCode, dto.getKeyword()).or().like(ProductInfo::getName, dto.getKeyword()).or().like(ProductInfo::getSpec, dto.getKeyword()));
            productInfoIds.add(null);//插入一个空元素防止为空in条件被忽略
            wrapper.in(MonthlyInventoryReport::getProductId, productInfoIds);
        }

        wrapper.orderByDesc(MonthlyInventoryReport::getId);

        //获取单位字典
        DynamicDataSourceContextHolder.push(SourceConstant.BASE);
        DictTenantDataSelectDto dto1 = new DictTenantDataSelectDto();
        dto1.setDictCode("unit");
        List<DictTenantDataVo> dataVoList = dictTenantDataService.getList(dto1);
        Map<String, String> unitMap = dataVoList.stream().collect(Collectors.toMap(DictTenantDataVo::getDictKey, DictTenantDataVo::getDictValue));
        DynamicDataSourceContextHolder.poll();

        List<MonthlyInventoryReport> list = this.list(wrapper);
        List<MonthlyInventoryReportVo> monthlyInventoryReportVos = BeanUtil.copyToList(list, MonthlyInventoryReportVo.class);
        //赋值产品名称
        productInfoService.attributeAssign(monthlyInventoryReportVos, MonthlyInventoryReportVo::getProductId, (item, productInfo) -> {
            item.setProductCode(productInfo.getCode());
            item.setProductName(productInfo.getName());
            item.setProductSpec(productInfo.getSpec());
            item.setProductUnit(unitMap.get(productInfo.getUnit()));
        });
        //赋值仓库名称
        warehouseService.attributeAssign(monthlyInventoryReportVos, MonthlyInventoryReportVo::getWarehouseId, (item, warehouse) -> {
            item.setWarehouseName(warehouse.getName());
        });

        //导出
        List<MonthlyInventoryReportExcel> monthlyInventoryReportExcels = BeanUtil.copyToList(monthlyInventoryReportVos, MonthlyInventoryReportExcel.class);
        ExcelUtil.export(httpServletResponse, monthlyInventoryReportExcels, MonthlyInventoryReportExcel.class);
    }

    /**
     * 获取日报已有年份列表
     */
    public List<String> getYearList() {
        return baseMapper.getYearList();
    }

}
