package com.fjhx.service.quality.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.Condition;
import com.fjhx.base.StorageBaseEntity;
import com.fjhx.constants.StatusConstant;
import com.fjhx.entity.material.Material;
import com.fjhx.entity.quality.QualityTesting;
import com.fjhx.entity.quality.QualityTestingDetails;
import com.fjhx.entity.water.WaterBatch;
import com.fjhx.entity.water.WaterTag;
import com.fjhx.enums.CheckJudgmentEnum;
import com.fjhx.enums.CheckStatusEnum;
import com.fjhx.enums.PurchaseProgressEnum;
import com.fjhx.mapper.quality.QualityTestingMapper;
import com.fjhx.service.contract.ContractService;
import com.fjhx.service.flow.CheckQuantityCorrectFlowService;
import com.fjhx.service.flow.InspectionExemptionFlowService;
import com.fjhx.service.material.MaterialService;
import com.fjhx.service.quality.QualityTestingDetailsService;
import com.fjhx.service.quality.QualityTestingService;
import com.fjhx.service.water.WaterBatchService;
import com.fjhx.service.water.WaterTagService;
import com.fjhx.utils.Assert;
import com.fjhx.utils.BigDecimalUtil;
import com.fjhx.utils.UserClientUtil;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.ThreadUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

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

/**
 * <p>
 * 质检 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-09-26
 */
@Service
public class QualityTestingServiceImpl extends ServiceImpl<QualityTestingMapper, QualityTesting> implements QualityTestingService {

    private static final Object EDIT_OBJ = new Object();

    @Autowired
    private MaterialService materialService;

    @Autowired
    private WaterBatchService waterBatchService;

    @Autowired
    @Lazy
    private QualityTestingDetailsService qualityTestingDetailsService;

    @Autowired
    private WaterTagService waterTagService;

    @Autowired
    @Lazy
    private CheckQuantityCorrectFlowService checkQuantityCorrectFlowService;

    @Autowired
    private ContractService contractService;

    @Autowired
    private InspectionExemptionFlowService inspectionExemptionFlowService;

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

        // 未完成质检
        QueryWrapper<Object> wrapper = Wrappers.query()
                .eq("qt.complete_status", StatusConstant.NO);

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

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

        // 获取第一次入库标签的批次号
        List<Long> waterBatchIdList = waterTagList.stream()
                .filter(waterTag -> waterTag.getPurchaseProgress() < PurchaseProgressEnum.WAREHOUSING.getType())
                .map(WaterTag::getWaterBatchId)
                .distinct()
                .collect(Collectors.toList());
        if (waterBatchIdList.size() == 0) return;

        // 查看已生成质检单的批次
        List<Long> list = list(Wrappers.<QualityTesting>lambdaQuery()
                .select(QualityTesting::getWaterBatchId)
                .in(QualityTesting::getWaterBatchId, waterBatchIdList)
        ).stream().map(QualityTesting::getWaterBatchId).collect(Collectors.toList());

        // 排除已经生成质检单的批次
        List<Long> needCreateBatchIdList = waterBatchIdList.stream().filter(item -> !list.contains(item)).collect(Collectors.toList());
        if (needCreateBatchIdList.size() == 0) return;

        // 获取批次id 物料id map
        Map<Long, Long> waterBatchIdMaterialIdMap = waterTagList.stream()
                .filter(item -> needCreateBatchIdList.contains(item.getWaterBatchId()))
                .collect(Collectors.toMap(WaterTag::getWaterBatchId, WaterTag::getMaterialId, (v1, v2) -> v1));

        // 批次id 入库卷数 map
        Map<Long, Integer> waterBatchIdTagCountMap = waterBatchService.listByIds(waterBatchIdMaterialIdMap.keySet())
                .stream().collect(Collectors.toMap(StorageBaseEntity::getId, WaterBatch::getTagCount));

        // 物料id 质检比例 map
        Map<Long, BigDecimal> materialCheckRateMap = materialService.list(Wrappers.<Material>lambdaQuery()
                .select(StorageBaseEntity::getId, Material::getCheckRate)
                .in(StorageBaseEntity::getId, new HashSet<>(waterBatchIdMaterialIdMap.values()))
        ).stream().collect(Collectors.toMap(StorageBaseEntity::getId, Material::getCheckRate));

        // 质检单
        List<QualityTesting> qualityTestingList = new ArrayList<>();
        waterBatchIdMaterialIdMap.forEach((batchId, materialId) -> {

            QualityTesting qualityTesting = new QualityTesting();
            qualityTesting.setCode(getCode());
            qualityTesting.setWaterBatchId(batchId);
            qualityTesting.setCompleteStatus(StatusConstant.NO);
            qualityTesting.setMaterialId(batchId);

            // 获取质检比例
            BigDecimal checkRate = materialCheckRateMap.get(materialId);

            // 入库数量
            Integer tagCount = waterBatchIdTagCountMap.get(batchId);

            int checkNum = BigDecimalUtil.multiply(checkRate, tagCount, 0, RoundingMode.CEILING).intValue();
            qualityTesting.setCheckNum(checkNum);
            qualityTesting.setHadCheckNum(0);
            qualityTesting.setCheckRate(checkRate);
            qualityTesting.setCheckQuantity(BigDecimal.ZERO);
            qualityTesting.setCheckArea(BigDecimal.ZERO);
            qualityTesting.setAdjustRate(BigDecimal.ZERO);

            qualityTestingList.add(qualityTesting);
        });

        // 添加质检单
        saveBatch(qualityTestingList);

        // 修改批次出货状态
        waterBatchService.updateShipmentStatus(waterBatchIdMaterialIdMap.keySet());

    }

    @Override
    public void edit(int changeNum, Long qualityTestingId) {

        synchronized (EDIT_OBJ) {

            // 质检单
            QualityTesting qualityTesting = getById(qualityTestingId);

            // 已质检卷数
            qualityTesting.setHadCheckNum(qualityTesting.getHadCheckNum() + changeNum);

            updateById(qualityTesting);

        }

    }

    @Override
    public Map<String, Object> details(Condition condition) {
        Long qualityTestingId = condition.getLong("qualityTestingId", "质检id不能为空");

        QualityTesting qualityTesting = getById(qualityTestingId);
        Assert.notEmpty(qualityTesting, "没有找到质检单");

        // 已质检卷数
        Integer hadCheckNum = qualityTesting.getHadCheckNum();

        // 需质检总卷数
        Integer checkNum = qualityTesting.getCheckNum();

        // 批次id
        Long waterBatchId = qualityTesting.getWaterBatchId();

        // 查看本批次在库的标签
        List<WaterTag> list = waterTagService.list(Wrappers.<WaterTag>lambdaQuery()
                .select(StorageBaseEntity::getId)
                .eq(WaterTag::getWaterBatchId, waterBatchId)
                .eq(WaterTag::getInHouse, StatusConstant.YES));

        // 质检明细列表
        List<QualityTestingDetails> qualityTestingDetailsList =
                qualityTestingDetailsService.list(QualityTestingDetails::getQualityTestingId, qualityTestingId);

        // 在库且未质检标签数量
        int qualityInspectionTagNum;

        // 排除已质检标签,算出还有多少标签可以质检
        if (qualityTestingDetailsList.size() == 0) {
            qualityInspectionTagNum = list.size();
        } else {
            // 已质检的标签id
            List<Long> waterTagIdList = qualityTestingDetailsList.stream()
                    .map(QualityTestingDetails::getWaterTagId).collect(Collectors.toList());

            qualityInspectionTagNum = Convert.toInt(list.stream().filter(item -> !waterTagIdList.contains(item.getId())).count());
        }

        Map<String, Object> result = new HashMap<>();
        result.put("checkNum", checkNum);
        result.put("needCheckNum", Math.max(checkNum - hadCheckNum, 0));
        result.put("qualityInspectionTagNum", qualityInspectionTagNum);
        result.put("inHouseTagNum", list.size());
        result.put("qualityTestingDetailsList", qualityTestingDetailsList);

        return result;
    }

    @Override
    public void submit(Condition condition) {
        Long qualityTestingId = condition.getLong("qualityTestingId", "质检id不能为空");
        Integer isInspectionExemption = condition.getInt("isInspectionExemption", "是否免检不能为空");
        String remark = condition.getStr("remark");

        if (StatusConstant.YES.equals(isInspectionExemption)) {
            inspectionExemptionFlowService.create(qualityTestingId, remark);
        }

        Date date = new Date();
        Long userId = AuthUtil.getUserId();

        // 质检
        QualityTesting qualityTesting = getById(qualityTestingId);

        // 质检明细列表
        List<QualityTestingDetails> qualityTestingDetailsList =
                qualityTestingDetailsService.list(QualityTestingDetails::getQualityTestingId, qualityTestingId);

        // 物料门幅
        BigDecimal width = materialService.getById(qualityTesting.getMaterialId()).getWidth();
        // 质检米数
        BigDecimal checkQuantity = BigDecimal.ZERO;
        // 质检判定
        Integer checkJudgment = CheckJudgmentEnum.QUALIFIED.getType();
        // 短少总米数
        BigDecimal lakeNumSum = BigDecimal.ZERO;
        // 质检标签id列表
        List<Long> tagIdList = new ArrayList<>();

        if (qualityTestingDetailsList.size() > 0) {

            // 循环质检明细列表
            for (QualityTestingDetails qualityTestingDetails : qualityTestingDetailsList) {
                // 质检数量
                checkQuantity = checkQuantity.add(qualityTestingDetails.getQuantity());
                // 短少数量
                lakeNumSum = lakeNumSum.add(qualityTestingDetails.getLakeNum());
                // 基本判定
                if (CheckJudgmentEnum.UNQUALIFIED.equals(CheckJudgmentEnum.get(qualityTestingDetails.getCheckJudgment()))) {
                    checkJudgment = CheckJudgmentEnum.UNQUALIFIED.getType();
                }
                tagIdList.add(qualityTestingDetails.getWaterTagId());
            }

            // 标签改为已质检
            waterTagService.update(Wrappers.<WaterTag>lambdaUpdate()
                    .in(StorageBaseEntity::getId, tagIdList)
                    .set(WaterTag::getCheckStatus, CheckStatusEnum.INSPECTED.getType())
                    .set(StorageBaseEntity::getUpdateTime, date)
                    .set(StorageBaseEntity::getUpdateUser, userId));
        }

        qualityTesting.setCompleteStatus(StatusConstant.YES);
        qualityTesting.setCheckUserId(AuthUtil.getUserId());
        qualityTesting.setCheckTime(date);
        qualityTesting.setCheckQuantity(checkQuantity);
        qualityTesting.setCheckArea(checkQuantity.multiply(width).divide(new BigDecimal(100), RoundingMode.HALF_UP));
        qualityTesting.setCheckJudgment(checkJudgment);
        qualityTesting.setAdjustRate(lakeNumSum.divide(checkQuantity, RoundingMode.HALF_UP));
        qualityTesting.setCheckInvalidRatio(getCheckInvalidRatio(date, qualityTesting.getCreateTime()));
        updateById(qualityTesting);

        // 同批次其他标签改为无需质检
        waterTagService.update(Wrappers.<WaterTag>lambdaUpdate()
                .eq(WaterTag::getWaterBatchId, qualityTesting.getWaterBatchId())
                .eq(WaterTag::getCheckStatus, CheckStatusEnum.NO_QUALITY_INSPECTION.getType())
                .set(WaterTag::getCheckStatus, CheckStatusEnum.NO_QUALITY_INSPECTION_REQUIRED.getType())
                .set(StorageBaseEntity::getUpdateTime, date)
                .set(StorageBaseEntity::getUpdateUser, userId));

        // 短少总数量大于0,发起 质检数量修正申请 审批流
        if (lakeNumSum.compareTo(BigDecimal.ZERO) > 0) {
            checkQuantityCorrectFlowService.create(qualityTestingId);
        } else {
            contractService.updateAdjustByQualityTestingId(qualityTestingId);
        }

    }

    @Override
    public Map<String, Object> recordStatistics(Condition condition) {
        QueryWrapper<Object> wrapper = Wrappers.query();
        creationTimeCondition(wrapper, condition);
        wrapper.eq("qt.complete_status", StatusConstant.YES)
                .eq("qt.del_flag", StatusConstant.NOT_DELETED);

        return baseMapper.recordStatistics(wrapper);
    }

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

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

        creationTimeCondition(wrapper, condition);

        wrapper.eq("qt.del_flag", StatusConstant.NOT_DELETED)
                .eq(ObjectUtil.isNotEmpty(completeStatus), "qt.complete_status", completeStatus)
                .eq(ObjectUtil.isNotEmpty(checkUserId), "qt.check_user_id", checkUserId)
                .and(ObjectUtil.isNotEmpty(keyword), q -> q
                        .like("wb.contract_code", keyword).or()
                        .like("m.code", keyword).or()
                        .like("m.name", keyword));

        Page<Map<String, Object>> page = baseMapper.recordPage(condition.getPage(), wrapper);
        List<Map<String, Object>> records = page.getRecords();
        if (records.size() == 0) {
            return page;
        }

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

        return page;
    }

    private static String getCode() {
        ThreadUtil.sleep(1);
        return "TP-" + DateUtil.format(new Date(), "yyMMdd-HHmmss-SSS");
    }

    /**
     * 计算质检及时率
     */
    private Integer getCheckInvalidRatio(Date date, Date createTime) {
        long day = DateUtil.betweenDay(createTime, date, false);

        if (day <= 3) {
            return 100;
        }

        return Math.max(Convert.toInt(100 - (day - 3) * 5), 0);
    }

    /**
     * 创建时间条件
     */
    private void creationTimeCondition(QueryWrapper<Object> wrapper, Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();

        wrapper.between(ObjectUtil.isAllNotEmpty(beginTime, endTime), "qt.create_time", beginTime, endTime);
    }

}
