package com.fjhx.service.contract.impl;

import cn.hutool.core.convert.Convert;
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.Condition;
import com.fjhx.base.StorageBaseEntity;
import com.fjhx.constants.StatusConstant;
import com.fjhx.entity.contract.Contract;
import com.fjhx.entity.contract.ContractDetails;
import com.fjhx.entity.water.WaterBatch;
import com.fjhx.entity.water.WaterTag;
import com.fjhx.enums.ContractStatusEnum;
import com.fjhx.enums.OutTypeEnum;
import com.fjhx.enums.PurchaseProgressEnum;
import com.fjhx.mapper.contract.ContractDetailsMapper;
import com.fjhx.service.contract.ContractDetailsService;
import com.fjhx.service.contract.ContractService;
import com.fjhx.service.water.WaterBatchService;
import com.fjhx.utils.Assert;
import com.fjhx.utils.BigDecimalUtil;
import com.fjhx.utils.UserClientUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 采购合同明细 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-09-19
 */
@Service
public class ContractDetailsServiceImpl extends ServiceImpl<ContractDetailsMapper, ContractDetails> implements ContractDetailsService {

    private static final Object UPDATE_SHIPMENT_QUANTITY_OBJ = new Object();

    // 到货数量超过采购数量百分之几算完成
    private static final BigDecimal PERCENT_QUALIFIED = new BigDecimal("0.95");

    @Autowired
    private WaterBatchService waterBatchService;

    @Autowired
    private ContractService contractService;

    @Override
    public void updateShipmentQuantity(Long waterBatchId) {
        WaterBatch waterBatch = waterBatchService.getById(waterBatchId);

        synchronized (UPDATE_SHIPMENT_QUANTITY_OBJ) {
            ContractDetails contractDetails = getById(waterBatch.getContractDetailsId());

            // 出货数量
            BigDecimal shipmentQuantity = contractDetails.getShipmentQuantity().add(waterBatch.getTagQuantitySum());

            // 更新合同出货数量
            update(Wrappers.<ContractDetails>lambdaUpdate()
                    .eq(StorageBaseEntity::getId, contractDetails.getId())
                    .set(ContractDetails::getShipmentQuantity, shipmentQuantity)
            );

            // TODO 出库数量大于百分之95,应供应商出货状态已完成,合同出货状态已完成(目前合同和明细为 1对1 关系,明细完成直接更新合同即可)
            if (BigDecimalUtil.ge(shipmentQuantity, contractDetails.getQuantity().multiply(PERCENT_QUALIFIED))) {
                contractService.update(Wrappers.<Contract>lambdaUpdate()
                        .eq(StorageBaseEntity::getId, contractDetails.getContractId())
                        .set(Contract::getShipmentStatus, StatusConstant.YES)
                );
            }
        }

    }

    @Override
    public void updateAfloatAndArrivalQuantity(List<WaterTag> waterTagList) {

        // 如果标签第一次入库，统计出所在合同明细id，值为改变数量
        Map<Long, BigDecimal> map = waterTagList.stream()
                .filter(waterTag -> waterTag.getPurchaseProgress() < PurchaseProgressEnum.WAREHOUSING.getType())
                .collect(Collectors.toMap(
                        WaterTag::getContractDetailsId,
                        WaterTag::getQuantity,
                        BigDecimal::add
                ));

        if (map.size() == 0) return;

        // 获取合同明细
        List<ContractDetails> contractDetailsList = listByIds(map.keySet());

        Date date = new Date();

        List<Contract> contractList = new ArrayList<>();

        // 循环标签列表所在合同
        for (ContractDetails contractDetails : contractDetailsList) {

            // 入库数量
            BigDecimal changeQuantity = map.get(contractDetails.getId());

            // 采购数量
            BigDecimal quantity = contractDetails.getQuantity();

            // 更新到货数量
            BigDecimal arrivalQuantity = contractDetails.getArrivalQuantity().add(changeQuantity);
            contractDetails.setArrivalQuantity(arrivalQuantity);

            // 如果到货数量大于等于采购数量的百分之95,合同状态改为已完成
            if (BigDecimalUtil.ge(arrivalQuantity, quantity.multiply(PERCENT_QUALIFIED))) {
                // 赋值完成时间
                contractDetails.setCompleteTime(date);
                // 明细合同已完成
                contractDetails.setCompleteStatus(StatusConstant.YES);
                // 在途为0
                contractDetails.setAfloatQuantity(BigDecimal.ZERO);

                // TODO 更新合同表(目前合同和明细为 1对1 关系,明细完成直接更新合同即可)
                Contract contract = new Contract();
                contract.setId(contractDetails.getContractId());
                contract.setStatus(ContractStatusEnum.COMPLETED.getType());
                contractDetails.setCompleteTime(date);
                contract.setShipmentStatus(StatusConstant.YES);
                contractList.add(contract);
            } else {
                // 更新在途数量
                BigDecimal afloatQuantity = contractDetails.getAfloatQuantity().subtract(changeQuantity);
                contractDetails.setAfloatQuantity(afloatQuantity);
            }

        }

        updateBatchById(contractDetailsList);
        if (contractList.size() > 0) {
            contractService.updateBatchById(contractList);
        }

    }

    @Override
    public void updateBackQuantity(List<WaterTag> waterTagList) {
        // 合同明细id,退货数量map
        Map<Long, BigDecimal> ContractDetailsIdBackQuantityMap = waterTagList.stream()
                .collect(Collectors.toMap(
                        WaterTag::getContractDetailsId,
                        WaterTag::getQuantity,
                        BigDecimal::add));

        // 合同明细列表
        List<ContractDetails> contractDetailsList = listByIds(ContractDetailsIdBackQuantityMap.keySet());

        // 更新退货数量
        contractDetailsList.forEach(item -> item.setBackQuantity(
                item.getBackQuantity().add(ContractDetailsIdBackQuantityMap.get(item.getId()))));

        updateBatchById(contractDetailsList);
    }

    @Override
    public Page<Map<String, Object>> getInPage(Condition condition) {
        String keyword = condition.getKeyword();
        Integer completeStatus = condition.getStatus();
        Long materialId = condition.getLong("materialId");

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

        wrapper.and(ObjectUtil.isNotEmpty(keyword),
                        q -> q.like("m.code", keyword).or().like("m.name", keyword))
                .eq(ObjectUtil.isNotEmpty(completeStatus), "cd.complete_status", completeStatus)
                .eq(ObjectUtil.isNotEmpty(materialId), "cd.material_id", materialId)
                .orderByDesc("cd.id");

        return baseMapper.getInPage(condition.getPage(), wrapper);
    }

    @Override
    public Map<String, Object> outStatistics(Condition condition) {
        QueryWrapper<Object> wrapper = getOutWrapper(condition);

        return baseMapper.outStatistics(wrapper);
    }

    @Override
    public Page<Map<String, Object>> getOutPage(Condition condition) {

        QueryWrapper<Object> wrapper = getOutWrapper(condition);
        wrapper.groupBy("w.id", "wd.material_id");

        Page<Map<String, Object>> page = baseMapper.getOutPage(condition.getPage(), wrapper);
        List<Map<String, Object>> records = page.getRecords();

        if (records.size() == 0) {
            return page;
        }

        // 赋值用户名称
        UserClientUtil.setUserName(records,"operUserId","operUserName");

        return page;
    }

    @Override
    public List<Map<String, Object>> getOutCount(Condition condition) {
        Long materialId = condition.getLong("materialId");
        Long waterId = condition.getLong("waterId");

        Assert.notEmpty(materialId, "物料id不能未空");
        Assert.notEmpty(waterId, "流水id不能未空");

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

        wrapper.eq("wd.water_id", waterId).eq("wd.material_id", materialId);

        return baseMapper.getOutCount(wrapper);
    }

    @Override
    public Page<Map<String, Object>> warehousingPage(Condition condition) {
        String keyword = condition.getKeyword();
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();

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

        wrapper.between(ObjectUtil.isAllNotEmpty(beginTime, endTime), "cd.update_time", beginTime, endTime)
                .and(ObjectUtil.isNotEmpty(keyword), q -> q
                        .like("m.name", keyword).or()
                        .like("n.code", keyword).or()
                        .like("cd.contract_code", keyword).or()
                        .like("s.name", keyword));

        return baseMapper.warehousingPage(condition.getPage(), wrapper);
    }

    private QueryWrapper<Object> getOutWrapper(Condition condition) {
        String keyword = condition.getKeyword();
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Long materialId = condition.getLong("materialId");

        // 出库类型
        Integer outType = condition.getInt("outType");
        // 操作人id
        Long operUserId = condition.getLong("operUserId");

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

        wrapper.and(ObjectUtil.isNotEmpty(keyword),
                        q -> q.like("m.name", keyword).or().like("m.code", keyword))
                .func(q -> {
                    if (ObjectUtil.isEmpty(outType)) {
                        q.in("w.type",
                                OutTypeEnum.ISSUE.getType(),
                                OutTypeEnum.RETURN_ISSUE.getType(),
                                OutTypeEnum.SCRAP_ISSUE.getType());
                    } else {
                        q.eq("w.type", outType);
                    }
                })
                .eq(ObjectUtil.isNotEmpty(operUserId), "w.oper_user_id", operUserId)
                .eq(ObjectUtil.isNotEmpty(materialId), "wd.material_id", materialId)
                .between(ObjectUtil.isAllNotEmpty(beginTime, endTime), "w.create_time", beginTime, endTime);

        return wrapper;
    }

}
