package com.fjhx.stock.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.base.BasicEntity;
import com.fjhx.constants.StatusConstant;
import com.fjhx.entity.material.Material;
import com.fjhx.entity.stock.StockBack;
import com.fjhx.entity.stock.StockBackDetails;
import com.fjhx.entity.stock.StockBackPlanDetails;
import com.fjhx.material.service.MaterialService;
import com.fjhx.params.stock.*;
import com.fjhx.plan.service.PlanConfigService;
import com.fjhx.stock.mapper.StockBackMapper;
import com.fjhx.stock.service.StockBackDetailsService;
import com.fjhx.stock.service.StockBackPlanDetailsService;
import com.fjhx.stock.service.StockBackService;
import com.fjhx.utils.Assert;
import com.fjhx.utils.BigDecimalUtil;
import com.fjhx.utils.WrapperUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 退仓面料 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-08-31
 */
@Service
public class StockBackServiceImpl extends ServiceImpl<StockBackMapper, StockBack> implements StockBackService {

    @Autowired
    private StockBackDetailsService stockBackDetailsService;

    @Autowired
    private MaterialService materialService;

    @Autowired
    private StockBackPlanDetailsService stockBackPlanDetailsService;

    @Autowired
    private PlanConfigService planConfigService;

    @Override
    public Page<StockBack> getPage(Map<String, String> condition) {

        QueryWrapper<StockBack> wrapper = Wrappers.query();

        WrapperUtil.init(condition, wrapper)
                .createTimeDesc();

        Page<StockBack> page = page(condition, wrapper);
        return page;
    }

    @Override
    public void add(StockBackVo stockBackVo) {
        save(stockBackVo);
    }

    @Override
    public void edit(StockBackVo stockBackVo) {
        updateById(stockBackVo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(Long stockBackId) {
        removeById(stockBackId);

        stockBackDetailsService.update(Wrappers.<StockBackDetails>lambdaUpdate()
                .eq(StockBackDetails::getStockBackId, stockBackId)
                .set(StockBackDetails::getMaterialMeters, BigDecimal.ZERO)
        );

    }

    @Override
    public List<Map<String, Object>> getPickingToday(Map<String, String> condition) {
        String userId = condition.get("userId");
        Assert.notEmpty(userId, "用户id不能为空");

        Date date = new Date();

        QueryWrapper<Object> wrapper = Wrappers.query()
                .eq("aa.AccountNo", userId)
                .in("swd.StockChangeType", 20, 23)
                .between("swd.CreatedTime", DateUtil.beginOfDay(date), DateUtil.endOfDay(date))
                .groupBy("m.id");

        return baseMapper.getPickingToday(wrapper);
    }

    @Override
    public List<Map<String, Object>> getPickingTodayByUserId(Map<String, String> condition) {
        String userId = condition.get("userId");
        Assert.notEmpty(userId, "用户id不能为空");

        Date date = new Date();

        QueryWrapper<Object> wrapper = Wrappers.query()
                .eq("swd.OperUserId", userId)
                .in("swd.StockChangeType", 20, 23)
                .between("swd.CreatedTime", DateUtil.beginOfDay(date), DateUtil.endOfDay(date))
                .groupBy("m.id");

        return baseMapper.getPickingTodayByUserId(wrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void submitBack(SubmitBackVo submitBackVo) {

        List<BackDetail> backDetailList = submitBackVo.getBackDetailList();
        String operatorId = submitBackVo.getOperatorId();
        Date date = new Date();

        List<StockBackDetails> stockBackDetailsList = new ArrayList<>();

        for (BackDetail backDetail : backDetailList) {
            List<BigDecimal> materialMetersList = backDetail.getMaterialMetersList();
            String materialId = backDetail.getMaterialId();

            // 面料退仓
            StockBack stockBack = new StockBack();
            stockBack.setType(submitBackVo.getType());
            stockBack.setOperatorId(operatorId);
            stockBack.setOperationTime(ObjectUtil.defaultIfNull(submitBackVo.getOperationTime(), date));
            stockBack.setOperationNum(materialMetersList.size());
            stockBack.setOperationMeters(materialMetersList.stream().reduce(BigDecimal.ZERO, BigDecimal::add));
            stockBack.setStatus(StatusConstant.NO);
            stockBack.setDelFlag(0);
            stockBack.setCreateTime(date);

            Date operationTime = stockBack.getOperationTime();
            if (Convert.toInt(DateUtil.format(operationTime, "H")) < 10) {
                stockBack.setStatisticsDate(DateUtil.offsetDay(operationTime, -1));
            } else {
                stockBack.setStatisticsDate(operationTime);
            }
            save(stockBack);

            // 退仓详情
            for (BigDecimal materialMeters : materialMetersList) {
                StockBackDetails stockBackDetails = new StockBackDetails();
                stockBackDetails.setStockBackId(stockBack.getId());
                stockBackDetails.setMaterialId(materialId);
                stockBackDetails.setMaterialMeters(materialMeters);
                stockBackDetails.setOldMaterialMeters(materialMeters);
                stockBackDetailsList.add(stockBackDetails);
            }

        }

        stockBackDetailsService.saveBatch(stockBackDetailsList);

    }

    @Override
    public List<Map<String, Object>> checkList() {
        return baseMapper.checkList();
    }

    @Override
    public CheckDetailsVo checkDetails(Long stockBackId) {

        CheckDetailsVo checkDetailsVo = new CheckDetailsVo();
        checkDetailsVo.setStockBackId(stockBackId);

        // 赋值操作人id
        StockBack stockBack = getById(stockBackId);
        checkDetailsVo.setOperatorId(stockBack.getOperatorId());

        List<StockBackDetails> list = stockBackDetailsService.list(StockBackDetails::getStockBackId, stockBackId);
        Map<String, List<StockBackDetails>> collect = list.stream().collect(Collectors.groupingBy(StockBackDetails::getMaterialId));

        // 通过物料id货物物料名称
        Map<String, Material> materialMap = materialService.listByIds(collect.keySet())
                .stream().collect(Collectors.toMap(BasicEntity::getId, item -> item));

        // 退仓面料信息
        List<CheckDetailsInfo> checkDetailsInfoList = new ArrayList<>();

        collect.forEach((k, v) -> {
            CheckDetailsInfo checkDetailsInfo = new CheckDetailsInfo();
            checkDetailsInfo.setMaterialId(k);
            checkDetailsInfo.setMaterialCode(materialMap.get(k).getCode());
            checkDetailsInfo.setMaterialName(materialMap.get(k).getName());
            checkDetailsInfo.setStockBackDetailsList(v);
            checkDetailsInfoList.add(checkDetailsInfo);
        });


        checkDetailsVo.setCheckDetailsInfoList(checkDetailsInfoList);

        return checkDetailsVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<StockBackDetails> submitCheckDetails(CheckDetailsVo checkDetailsVo) {

        Long stockBackId = checkDetailsVo.getStockBackId();
        String checkerId = checkDetailsVo.getCheckerId();

        List<CheckDetailsInfo> checkDetailsInfoList = checkDetailsVo.getCheckDetailsInfoList();

        List<StockBackDetails> stockBackDetails = new ArrayList<>();

        // 循环退仓面料信息
        for (CheckDetailsInfo checkDetailsInfo : checkDetailsInfoList) {
            String materialId = checkDetailsInfo.getMaterialId();
            List<StockBackDetails> stockBackDetailsList = checkDetailsInfo.getStockBackDetailsList();

            // 循环得到具体某一卷物料
            for (StockBackDetails backDetails : stockBackDetailsList) {

                backDetails.setMaterialId(materialId);

                // id为空表示新增，赋值信息
                if (ObjectUtil.isEmpty(backDetails.getId())) {
                    backDetails.setStockBackId(stockBackId);
                    backDetails.setOldMaterialMeters(BigDecimal.ZERO);
                }
            }
            stockBackDetails.addAll(stockBackDetailsList);
        }

        // 新增或编辑
        stockBackDetailsService.saveOrUpdateBatch(stockBackDetails);

        // 后端存在，前端没传入的数据代表已被删除，把米数改为0
        stockBackDetailsService.update(Wrappers.<StockBackDetails>lambdaUpdate()
                .eq(StockBackDetails::getStockBackId, stockBackId)
                .notIn(StockBackDetails::getId, stockBackDetails.stream().map(StockBackDetails::getId).collect(Collectors.toList()))
                .set(StockBackDetails::getMaterialMeters, BigDecimal.ZERO)
        );

        // 修改退仓复核信息
        update(Wrappers.<StockBack>lambdaUpdate()
                .eq(StockBack::getId, stockBackId)
                .set(StockBack::getCheckerId, checkerId)
                .set(StockBack::getCheckerTime, new Date())
                .set(StockBack::getStatus, StatusConstant.YES)
        );

        return stockBackDetails;
    }

    @Override
    public Map<String, List<Map<String, Object>>> userStatistics(Map<String, String> condition) {

        String beginTimeStr = condition.get("beginTime");
        Assert.notEmpty(beginTimeStr, "开始时间不能为空");
        Date beginTime = DateUtil.beginOfDay(DateUtil.parse(beginTimeStr));

        String endTimeStr = condition.get("endTime");
        Assert.notEmpty(endTimeStr, "结束时间不能为空");
        Date endTime = DateUtil.endOfDay(DateUtil.parse(endTimeStr));

        List<Map<String, Object>> result = userList();

        List<StockBackPlanDetails> planList = stockBackPlanDetailsService.list(Wrappers.<StockBackPlanDetails>lambdaQuery()
                .between(StockBackPlanDetails::getPlanTime, beginTime, endTime));

        Map<String, List<StockBackPlanDetails>> planGroupByJobNo = planList.stream().collect(Collectors.groupingBy(StockBackPlanDetails::getJobNo));

        for (Map<String, Object> map : result) {

            String jobNo = map.get("jobNo").toString();

            List<StockBackPlanDetails> stockBackPlanDetails = planGroupByJobNo.get(jobNo);

            if (stockBackPlanDetails != null && stockBackPlanDetails.size() > 0) {
                BigDecimal scheduleArea = BigDecimal.ZERO;
                BigDecimal pickingArea = BigDecimal.ZERO;
                BigDecimal backArea = BigDecimal.ZERO;
                BigDecimal correctArea = BigDecimal.ZERO;
                BigDecimal headTailCorrectArea = BigDecimal.ZERO;

                for (StockBackPlanDetails item : stockBackPlanDetails) {
                    scheduleArea = scheduleArea.add(item.getQuantity());
                    pickingArea = pickingArea.add(item.getPickingArea()).add(ObjectUtil.defaultIfNull(item.getTransferInArea(), BigDecimal.ZERO));
                    backArea = backArea.add(item.getBackArea()).add(ObjectUtil.defaultIfNull(item.getTransferOutArea(), BigDecimal.ZERO));
                    correctArea = correctArea.add(ObjectUtil.defaultIfNull(item.getCorrectArea(), BigDecimal.ZERO));
                    headTailCorrectArea = headTailCorrectArea.add(ObjectUtil.defaultIfNull(item.getHeadTailCorrectArea(), BigDecimal.ZERO));
                }

                // 赋值使用率
                BigDecimal value = BigDecimalUtil.init(scheduleArea).multiply(100).
                        divideTry(pickingArea.subtract(backArea).subtract(correctArea).subtract(headTailCorrectArea), 2).getValue();

                map.put("useRate", value);
            } else {
                map.put("useRate", BigDecimal.ZERO);
            }

        }

        return result.stream().collect(Collectors.groupingBy(item -> (String) item.get("departmentID")));
    }

    @Override
    public Map<String, Object> techStatistics(Map<String, String> condition) {
        String beginTimeStr = condition.get("beginTime");
        Assert.notEmpty(beginTimeStr, "开始时间不能为空");
        Date beginTime = DateUtil.beginOfDay(DateUtil.parse(beginTimeStr));

        String endTimeStr = condition.get("endTime");
        Assert.notEmpty(endTimeStr, "结束时间不能为空");
        Date endTime = DateUtil.endOfDay(DateUtil.parse(endTimeStr));

        List<Map<String, Object>> userList = baseMapper.userStatistics(
                Wrappers.query().eq("uu.IsDelete", 0).isNotNull("uu.JobNo"));

        List<StockBackPlanDetails> planList = stockBackPlanDetailsService.list(Wrappers.<StockBackPlanDetails>lambdaQuery()
                .between(StockBackPlanDetails::getPlanTime, beginTime, endTime)
                .ne(StockBackPlanDetails::getTech, "")
                .isNotNull(StockBackPlanDetails::getTech)
        );

        // 用户按工号分组
        Map<String, Map<String, Object>> jobNoMap = userList.stream().collect(Collectors.toMap(
                item -> item.get("jobNo").toString(),
                item -> item
        ));

        // 先按工艺归类，再按用户归类
        Map<String, Map<String, List<StockBackPlanDetails>>> collect = planList.stream().collect(Collectors.groupingBy(
                StockBackPlanDetails::getTech,
                Collectors.groupingBy(StockBackPlanDetails::getJobNo)
        ));

        Map<String, Object> result = new LinkedHashMap<>();

        collect.forEach((k, v) -> {
            List<Map<String, Object>> list = new ArrayList<>();

            HashMap<String, BigDecimal> totalMap = new HashMap<>();
            totalMap.put("totalScheduleArea", BigDecimal.ZERO);
            totalMap.put("totalPickingArea", BigDecimal.ZERO);
            totalMap.put("totalBackArea", BigDecimal.ZERO);
            totalMap.put("totalCorrectArea", BigDecimal.ZERO);
            totalMap.put("totalHeadTailCorrectArea", BigDecimal.ZERO);

            v.forEach((k2, v2) -> {
                Map<String, Object> map = jobNoMap.get(k2);
                if (map != null) {
                    Map<String, Object> itemMap = new HashMap<>(map);

                    if (v2 != null && v2.size() > 0) {
                        BigDecimal scheduleArea = BigDecimal.ZERO;
                        BigDecimal pickingArea = BigDecimal.ZERO;
                        BigDecimal backArea = BigDecimal.ZERO;
                        BigDecimal correctArea = BigDecimal.ZERO;
                        BigDecimal headTailCorrectArea = BigDecimal.ZERO;

                        for (StockBackPlanDetails item : v2) {
                            scheduleArea = scheduleArea.add(item.getQuantity());
                            pickingArea = pickingArea.add(item.getPickingArea()).add(ObjectUtil.defaultIfNull(item.getTransferInArea(), BigDecimal.ZERO));
                            backArea = backArea.add(item.getBackArea()).add(ObjectUtil.defaultIfNull(item.getTransferOutArea(), BigDecimal.ZERO));
                            correctArea = correctArea.add(item.getCorrectArea());
                            headTailCorrectArea = headTailCorrectArea.add(item.getHeadTailCorrectArea());
                        }

                        totalMap.put("totalScheduleArea", totalMap.get("totalScheduleArea").add(scheduleArea));
                        totalMap.put("totalPickingArea", totalMap.get("totalPickingArea").add(pickingArea));
                        totalMap.put("totalBackArea", totalMap.get("totalBackArea").add(backArea));
                        totalMap.put("totalCorrectArea", totalMap.get("totalCorrectArea").add(correctArea));
                        totalMap.put("totalHeadTailCorrectArea", totalMap.get("totalHeadTailCorrectArea").add(headTailCorrectArea));

                        // 赋值使用率
                        BigDecimal value = BigDecimalUtil.init(scheduleArea)
                                .multiply(100)
                                .divideTry(pickingArea.subtract(backArea).subtract(correctArea).subtract(headTailCorrectArea), 2)
                                .getValue();

                        itemMap.put("useRate", value);
                    } else {
                        itemMap.put("useRate", BigDecimal.ZERO);
                    }
                    list.add(itemMap);
                }

            });

            // 赋值使用率
            BigDecimal value = BigDecimalUtil.init(totalMap.get("totalScheduleArea"))
                    .multiply(100)
                    .divideTry(totalMap.get("totalPickingArea")
                            .subtract(totalMap.get("totalBackArea"))
                            .subtract(totalMap.get("totalCorrectArea"))
                            .subtract(totalMap.get("totalHeadTailCorrectArea")), 2)
                    .getValue();

            result.put(k, list);
            result.put(k + "use", value);
        });

        return result;
    }

    @Override
    public StatisticsResult statisticsDetails(Map<String, String> condition) {

        StatisticsResult result = new StatisticsResult();

        String jobNo = condition.get("jobNo");

        String beginTimeStr = condition.get("beginTime");
        Assert.notEmpty(beginTimeStr, "开始时间不能为空");
        Date beginTime = DateUtil.beginOfDay(DateUtil.parse(beginTimeStr));

        String endTimeStr = condition.get("endTime");
        Assert.notEmpty(endTimeStr, "结束时间不能为空");
        Date endTime = DateUtil.endOfDay(DateUtil.parse(endTimeStr));

        String tech = condition.get("tech");

        List<StockBackPlanDetails> list = stockBackPlanDetailsService.list(
                Wrappers.<StockBackPlanDetails>lambdaQuery()
                        .between(StockBackPlanDetails::getPlanTime, beginTime, endTime)
                        .func(q -> {
                            if (ObjectUtil.isNotEmpty(jobNo)) {
                                q.eq(StockBackPlanDetails::getJobNo, jobNo);
                            } else {
                                List<Object> jobNoList = userList().stream().map(item -> item.get("jobNo")).collect(Collectors.toList());
                                q.in(StockBackPlanDetails::getJobNo, jobNoList);
                            }
                        })
                        .eq(ObjectUtil.isNotEmpty(tech), StockBackPlanDetails::getTech, tech)
        );

        for (StockBackPlanDetails item : list) {
            result.setScheduleArea(result.getScheduleArea().add(item.getQuantity()));
            result.setCorrectArea(result.getCorrectArea().add(item.getCorrectArea()));
            result.setPickingMoney(result.getPickingMoney().add(item.getPickingMoney()));
            result.setPickingNum(result.getPickingNum() + item.getPickingNum());
            result.setPickingMeters(result.getPickingMeters().add(item.getPickingMeters()));
            result.setPickingArea(result.getPickingArea().add(item.getPickingArea()));
            result.setBackMoney(result.getBackMoney().add(item.getBackMoney()));
            result.setBackNum(result.getBackNum() + item.getBackNum());
            result.setBackMeters(result.getBackMeters().add(item.getBackMeters()));
            result.setBackArea(result.getBackArea().add(item.getBackArea()));
            result.setTransferInArea(result.getTransferInArea().add(ObjectUtil.defaultIfNull(item.getTransferInArea(), BigDecimal.ZERO)));
            result.setTransferOutArea(result.getTransferOutArea().add(ObjectUtil.defaultIfNull(item.getTransferOutArea(), BigDecimal.ZERO)));
            result.setHeadTailCorrectArea(result.getHeadTailCorrectArea().add(ObjectUtil.defaultIfNull(item.getHeadTailCorrectArea(), BigDecimal.ZERO)));
        }

        return result;
    }

    @Override
    public Page<StatisticsResult> statisticsPage(Map<String, String> condition) {

        String jobNo = condition.get("jobNo");

        String beginTimeStr = condition.get("beginTime");
        Assert.notEmpty(beginTimeStr, "开始时间不能为空");
        Date beginTime = DateUtil.beginOfDay(DateUtil.parse(beginTimeStr));

        String endTimeStr = condition.get("endTime");
        Assert.notEmpty(endTimeStr, "结束时间不能为空");
        Date endTime = DateUtil.endOfDay(DateUtil.parse(endTimeStr));

        String tech = condition.get("tech");

        return baseMapper.selectPickingPage(createPage(condition), jobNo, beginTime, endTime, tech);
    }

    @Override
    public Map<String, List<Map<String, Object>>> getUser(Map<String, String> condition) {
        return userList().stream().collect(Collectors.groupingBy(item -> (String) item.get("departmentID")));
    }

    @Override
    public Map<String, Object> getBackStatistics(Map<String, String> condition) {
        return baseMapper.getBackStatistics(getBackWrapper(condition));
    }

    @Override
    public Page<Map<String, Object>> getBackPage(Map<String, String> condition) {
        return baseMapper.getBackPage(createPage(condition), getBackWrapper(condition));
    }


    private List<Map<String, Object>> userList() {

        QueryWrapper<Object> wrapper = Wrappers.query()
                .eq("uu.IsDelete", 0)
                .in("uu.DepartmentID", "A007", "A008", "A012", "A017")
                .isNotNull("uu.JobNo");

        return baseMapper.userStatistics(wrapper);
    }

    private QueryWrapper<Object> getBackWrapper(Map<String, String> condition) {
        String userId = condition.get("userId");

        String beginTimeStr = condition.get("beginTime");
        Assert.notEmpty(beginTimeStr, "开始时间不能为空");
        Date beginTime = DateUtil.beginOfDay(DateUtil.parse(beginTimeStr));

        String endTimeStr = condition.get("endTime");
        Assert.notEmpty(endTimeStr, "结束时间不能为空");
        Date endTime = DateUtil.endOfDay(DateUtil.parse(endTimeStr));

        String status = condition.get("status");

        String userName = condition.get("userName");
        String materialName = condition.get("materialName");

        return Wrappers.query()
                .between("sb.operation_time", beginTime, endTime)
                .eq(ObjectUtil.isNotEmpty(userId), "ac.ID", userId)
                .func(ObjectUtil.isNotEmpty(status), q -> {
                    if (Objects.equals(status, "0") || Objects.equals(status, "1")) {
                        q.eq("sb.status", status);
                    } else {
                        q.eq("sbd.flow_status", 0);
                    }
                })
                .like(ObjectUtil.isNotEmpty(userName), "uu.RealName", userName)
                .and(ObjectUtil.isNotEmpty(materialName),
                        q -> q.like("m.Name", materialName).or().like("m.code", materialName))
                .eq("sb.del_flag", 0);
    }

}
