package com.fjhx.service.check.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
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.HouseConstant;
import com.fjhx.constants.StatusConstant;
import com.fjhx.constants.WebSocketConstant;
import com.fjhx.entity.check.CheckDetail;
import com.fjhx.entity.check.CheckInfo;
import com.fjhx.entity.check.CheckTag;
import com.fjhx.entity.stock.Stock;
import com.fjhx.entity.water.WaterTag;
import com.fjhx.mapper.check.CheckInfoMapper;
import com.fjhx.params.check.CheckDetailVo;
import com.fjhx.params.check.CheckInfoEx;
import com.fjhx.service.WebSocketServer;
import com.fjhx.service.check.CheckDetailService;
import com.fjhx.service.check.CheckInfoService;
import com.fjhx.service.check.CheckTagService;
import com.fjhx.service.stock.StockService;
import com.fjhx.service.water.WaterTagService;
import com.fjhx.utils.Assert;
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.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-11-01
 */
@Service
public class CheckInfoServiceImpl extends ServiceImpl<CheckInfoMapper, CheckInfo> implements CheckInfoService {

    /**
     * 上位机读取的rfid
     */
    public static final List<String> RFID_LIST = Collections.synchronizedList(new ArrayList<>());

    @Autowired
    private WaterTagService waterTagService;

    @Autowired
    private StockService stockService;

    @Autowired
    private CheckDetailService checkDetailService;

    @Autowired
    private CheckTagService checkTagService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized void automaticCheck() {

        // 发起标签扫描
        JSONObject data = new JSONObject();
        data.put("instructions", "test");
        data.put("type", 2);
        data.put("flag", "automaticCheck");
        WebSocketServer.sendInfo(WebSocketConstant.WEB_STOCK_PROGRAM, WebSocketConstant.OPERATING_UPPER_COMPUTER, data);

        // 扫描10分钟
        ThreadUtil.sleep(1000 * 60 * 10);

        // 关闭扫描标签
        data.clear();
        data.put("type", 9);
        WebSocketServer.sendInfo(WebSocketConstant.WEB_STOCK_PROGRAM, WebSocketConstant.OPERATING_UPPER_COMPUTER, data);

        // 获取在库标签
        List<WaterTag> list = getInHouseWaterTag();

        // 获取扫描到的去重标签
        List<String> rfidList = RFID_LIST.stream().distinct().collect(Collectors.toList());
        RFID_LIST.clear();

        // 异常物料id
        List<Long> errorMaterialId = getErrorMaterialId(list, rfidList);

        // 保存盘点结果
        Long checkId = savaCheck(errorMaterialId);

        if (errorMaterialId.size() > 0) {
            // 保存盘点结果明细
            savaCheckDetailList(errorMaterialId, checkId);
        }

    }

    @Override
    public Page<Map<String, Object>> getPage(Condition condition) {
        String keyword = condition.getKeyword();
        Integer status = condition.getStatus();
        Integer type = condition.getType();

        QueryWrapper<Object> wrapper = Wrappers.query();
        wrapper.and(ObjectUtil.isNotEmpty(keyword),
                        q -> q.like("m.name", keyword).or().like("m.code", keyword))
                .eq(ObjectUtil.isNotEmpty(status), "cd.complete_check", status)
                .eq(ObjectUtil.isNotEmpty(type), "ci.type", type);

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

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void submit(CheckDetailVo checkDetailVo) {

        checkDetailVo.setCompleteCheck(StatusConstant.YES);
        checkDetailVo.setCheckTime(new Date());
        checkDetailService.updateById(checkDetailVo);

        List<CheckTag> checkTagList = checkDetailVo.getCheckTagList();
        checkTagService.saveBatch(checkTagList);

        // 检测是否完成全部盘点
        synchronized (this) {
            Long count = checkDetailService.count(q -> q
                    .eq(CheckDetail::getCheckId, checkDetailVo.getCheckId())
                    .eq(CheckDetail::getCompleteCheck, StatusConstant.NO));

            // 盘点完成
            if (count == 0) {
                update(q -> q
                        .eq(StorageBaseEntity::getId, checkDetailVo.getCheckId())
                        .set(CheckInfo::getStatus, 2)
                        .set(checkDetailVo.getCheckResult() == 0, CheckInfo::getPeopleCheckResult, 0)
                        .set(CheckInfo::getOperUserId, AuthUtil.getUserId())
                        .set(CheckInfo::getEndTime, new Date())
                        .set(StorageBaseEntity::getUpdateTime, new Date())
                        .set(StorageBaseEntity::getUpdateUser, AuthUtil.getUserId())
                );
            } else {
                update(q -> q
                        .eq(StorageBaseEntity::getId, checkDetailVo.getCheckId())
                        .set(CheckInfo::getStatus, 1)
                        .set(checkDetailVo.getCheckResult() == 0, CheckInfo::getPeopleCheckResult, 0)
                        .set(CheckInfo::getOperUserId, AuthUtil.getUserId())
                        .set(StorageBaseEntity::getUpdateTime, new Date())
                        .set(StorageBaseEntity::getUpdateUser, AuthUtil.getUserId())
                );
            }
        }
    }

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

        QueryWrapper<Object> wrapper = Wrappers.query().between("ci.create_time", beginTime, endTime);

        return baseMapper.monthlyStatistics(wrapper);
    }

    @Override
    public Long getCount(Condition condition) {
        return count(q -> q.between(StorageBaseEntity::getCreateTime, condition.getBeginTime(), condition.getEndTime()));
    }

    @Override
    public List<Map<String, Object>> houseCount(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Integer type = condition.getType();
        Integer peopleCheckResult = condition.getInt("peopleCheckResult");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .between("ci.create_time", beginTime, endTime)
                .eq(ObjectUtil.isNotEmpty(type), "ci.type", type)
                .eq(ObjectUtil.isNotEmpty(peopleCheckResult), "ci.people_check_result", peopleCheckResult)
                .groupBy("ci.stock_house_id");

        return baseMapper.houseCount(wrapper);
    }

    @Override
    public Map<String, Object> typeCount(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Long houseId = condition.getLong("houseId");
        Integer peopleCheckResult = condition.getInt("peopleCheckResult");

        return getMap(Wrappers.<CheckInfo>query()
                .select(
                        "ifnull(sum(if(type = 1, 1, 0)), 0) automaticCheckCount",
                        "ifnull(sum(if(type = 2, 1, 0)), 0) userCheckCount"
                )
                .lambda()
                .between(StorageBaseEntity::getCreateTime, beginTime, endTime)
                .eq(ObjectUtil.isNotEmpty(peopleCheckResult), CheckInfo::getPeopleCheckResult, peopleCheckResult)
                .eq(ObjectUtil.isNotEmpty(houseId), CheckInfo::getStockHouseId, houseId)
        );
    }

    @Override
    public Map<String, Object> peopleCheckResultCount(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Long houseId = condition.getLong("houseId");
        Integer type = condition.getType();

        return getMap(Wrappers.<CheckInfo>query()
                .select(
                        "ifnull(sum(if(people_check_result = 1, 1, 0)), 0) normalCount",
                        "ifnull(sum(if(people_check_result = 0, 1, 0)), 0) errorCount"
                )
                .lambda()
                .between(StorageBaseEntity::getCreateTime, beginTime, endTime)
                .eq(ObjectUtil.isNotEmpty(type), CheckInfo::getType, type)
                .eq(ObjectUtil.isNotEmpty(houseId), CheckInfo::getStockHouseId, houseId)
        );
    }

    @Override
    public Page<Map<String, Object>> webPage(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Integer type = condition.getType();
        Integer peopleCheckResult = condition.getInt("peopleCheckResult");
        Long houseId = condition.getLong("houseId");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .between("ci.create_time", beginTime, endTime)
                .eq(ObjectUtil.isNotEmpty(type), "ci.type", type)
                .eq(ObjectUtil.isNotEmpty(peopleCheckResult), "ci.people_check_result", peopleCheckResult)
                .eq(ObjectUtil.isNotEmpty(houseId), "ci.stock_house_id", houseId);

        Page<Map<String, Object>> page = baseMapper.webPage(condition.getPage(), wrapper);
        UserClientUtil.setUserName(page.getRecords(), "operUserId", "operUserName");
        return page;
    }

    @Override
    public List<Map<String, Object>> userCount(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();

        List<Map<String, Object>> list = listMaps(Wrappers.<CheckInfo>query()
                .select(
                        "oper_user_id",
                        "count(0) count"
                )
                .lambda()
                .isNotNull(CheckInfo::getOperUserId)
                .between(StorageBaseEntity::getCreateTime, beginTime, endTime)
                .groupBy(CheckInfo::getOperUserId)
        );
        UserClientUtil.setUserName(list, "operUserId", "operUserName");
        return list;
    }

    @Override
    public List<Map<String, Object>> errorTagTechnologyTypeCount(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();

        QueryWrapper<Object> wrapper = Wrappers.query()
                .between("ci.create_time", beginTime, endTime)
                .ne("ct.tag_status", 0)
                .groupBy("m.technology_type")
                .orderByAsc("m.technology_type");

        return baseMapper.errorTagTechnologyTypeCount(wrapper);
    }

    @Override
    public List<Map<String, Object>> errorTagPurposeCount(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Integer technologyType = condition.getInt("technologyType");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .between("ci.create_time", beginTime, endTime)
                .ne("ct.tag_status", 0)
                .eq(ObjectUtil.isNotEmpty(technologyType), "m.technology_type", technologyType)
                .groupBy("m.purpose");

        return baseMapper.errorTagPurposeCount(wrapper);
    }

    @Override
    public List<Map<String, Object>> errorTagHandleTypeCount(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Integer technologyType = condition.getInt("technologyType");
        String purpose = condition.getStr("purpose");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .between("ci.create_time", beginTime, endTime)
                .ne("ct.tag_status", 0)
                .eq(ObjectUtil.isNotEmpty(technologyType), "m.technology_type", technologyType)
                .eq(ObjectUtil.isNotEmpty(purpose), "m.purpose", purpose)
                .groupBy("ct.handle_type");

        return baseMapper.errorTagHandleTypeCount(wrapper);

    }

    @Override
    public Page<Map<String, Object>> errorTagPage(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Integer technologyType = condition.getInt("technologyType");
        String purpose = condition.getStr("purpose");
        Integer handleType = condition.getInt("handleType");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .between("ci.create_time", beginTime, endTime)
                .ne("ct.tag_status", 0)
                .eq(ObjectUtil.isNotEmpty(technologyType), "m.technology_type", technologyType)
                .eq(ObjectUtil.isNotEmpty(purpose), "m.purpose", purpose)
                .eq(ObjectUtil.isNotEmpty(handleType), "ct.handle_type", handleType);

        Page<Map<String, Object>> page = baseMapper.errorTagPage(condition.getPage(), wrapper);

        UserClientUtil.setUserName(page.getRecords(), "operUserId", "operUserName");
        return page;
    }

    @Override
    public List<String> errorMaterialPurpose(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Integer technologyType = condition.getInt("technologyType");
        Long checkInfoId = condition.getLong("checkInfoId");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .between(ObjectUtil.isAllNotEmpty(beginTime, endTime), "ci.create_time", beginTime, endTime)
                .eq(ObjectUtil.isNotEmpty(technologyType), "m.technology_type", technologyType)
                .eq(ObjectUtil.isEmpty(technologyType), "cd.check_result", 0)
                .eq(ObjectUtil.isNotEmpty(checkInfoId), "ci.id", checkInfoId);

        return baseMapper.errorMaterialPurpose(wrapper);
    }

    @Override
    public Page<Map<String, Object>> errorMaterialPage(Condition condition) {
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        Integer technologyType = condition.getInt("technologyType");
        String purpose = condition.getStr("purpose");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .between(ObjectUtil.isAllNotEmpty(beginTime, endTime), "ci.create_time", beginTime, endTime)
                .eq(ObjectUtil.isNotEmpty(technologyType), "m.technology_type", technologyType)
                .eq(ObjectUtil.isNotEmpty(purpose), "m.purpose", purpose)
                .groupBy("m.id");

        Page<Map<String, Object>> page = baseMapper.errorMaterialPage(condition.getPage(), wrapper);
        UserClientUtil.setUserName(page.getRecords(), "operUserId", "operUserName");
        return page;
    }

    @Override
    public Page<Map<String, Object>> errorMaterialTagPage(Condition condition) {
        Long id = condition.getLong("id");

        QueryWrapper<Object> wrapper = Wrappers.query();
        wrapper.eq("cd.id", id);

        Page<Map<String, Object>> page = baseMapper.errorMaterialTagPage(condition.getPage(), wrapper);
        UserClientUtil.setUserName(page.getRecords(), "operUserId", "operUserName");
        return page;
    }

    @Override
    public Map<String, Object> checkResultStatistics(Condition condition) {
        Long checkInfoId = condition.getLong("checkInfoId");
        Integer technologyType = condition.getInt("technologyType");
        String purpose = condition.getStr("purpose");

        Assert.notEmpty(checkInfoId, "盘点id不能为空");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .eq("ci.id", checkInfoId)
                .eq(ObjectUtil.isNotEmpty(technologyType), "m.technology_type", technologyType)
                .eq(ObjectUtil.isNotEmpty(purpose), "m.purpose", purpose);


        return baseMapper.checkResultStatistics(wrapper);
    }

    @Override
    public Page<Map<String, Object>> materialDetailPage(Condition condition) {
        Long checkInfoId = condition.getLong("checkInfoId");
        Integer technologyType = condition.getInt("technologyType");
        String purpose = condition.getStr("purpose");
        Integer checkResult = condition.getInt("checkResult");

        Assert.notEmpty(checkInfoId, "盘点id不能为空");

        QueryWrapper<Object> wrapper = Wrappers.query()
                .eq("ci.id", checkInfoId)
                .eq(ObjectUtil.isNotEmpty(technologyType), "m.technology_type", technologyType)
                .eq(ObjectUtil.isNotEmpty(purpose), "m.purpose", purpose)
                .eq(ObjectUtil.isNotEmpty(checkResult), "cd.check_result", checkResult);

        Page<Map<String, Object>> page = baseMapper.materialDetailPage(condition.getPage(), wrapper);
        UserClientUtil.setUserName(page.getRecords(), "operUserId", "operUserName");
        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void initiateManualCounting(CheckInfoEx checkInfoEx) {
        Assert.notEmpty(checkInfoEx.getOperUserId(), "操作人id不能为空");
        Assert.notEmpty(checkInfoEx.getBeginTime(), "开始盘点时间不能为空");

        List<Long> materialIdList = checkInfoEx.getMaterialIdList();
        Assert.notEmpty(materialIdList, "物料id列表不能为空");

        checkInfoEx.setCheckResult(StatusConstant.YES);
        checkInfoEx.setType(2);
        checkInfoEx.setStatus(0);
        checkInfoEx.setPeopleCheckResult(1);
        save(checkInfoEx);

        List<Stock> stockList = stockService.list(q -> q.in(Stock::getMaterialId, materialIdList));
        Map<Long, Stock> collect = stockList.stream().collect(Collectors.toMap(Stock::getMaterialId, item -> item));

        List<CheckDetail> checkDetailList = materialIdList.stream().map(item -> {
            CheckDetail checkDetail = new CheckDetail();
            checkDetail.setCheckId(checkInfoEx.getId());
            checkDetail.setMaterialId(item);

            Stock stock = collect.get(item);
            if (stock == null) {
                checkDetail.setStockCount(0);
                checkDetail.setStockQuantity(BigDecimal.ZERO);
            } else {
                checkDetail.setStockCount(stock.getNumber());
                checkDetail.setStockQuantity(stock.getQuantity());
            }
            checkDetail.setCheckResult(1);
            return checkDetail;
        }).collect(Collectors.toList());

        checkDetailService.saveBatch(checkDetailList);
    }

    /**
     * 获取在库标签
     */
    private List<WaterTag> getInHouseWaterTag() {
        return waterTagService.list(q -> q
                .eq(WaterTag::getStockHouseId, HouseConstant.GROUND_FLOOR_ID)
                .eq(WaterTag::getInHouse, StatusConstant.YES)
                .eq(WaterTag::getUseStatus, StatusConstant.YES)
        );
    }

    /**
     * 获取异常物料id
     */
    private List<Long> getErrorMaterialId(List<WaterTag> list, List<String> rfidList) {
        // 异常物料id
        HashSet<Long> errorMaterialIdList = new HashSet<>();

        // 在库存，但没有扫描到rfid
        for (WaterTag waterTag : list) {
            String rfidCode = waterTag.getRfidCode();
            if (rfidList.contains(rfidCode)) {
                rfidList.remove(rfidCode);
            } else {
                errorMaterialIdList.add(waterTag.getMaterialId());
            }
        }

        // 不在库存，但扫描到rfid
        errorMaterialIdList.addAll(
                waterTagService.list(q -> q
                                .select(WaterTag::getMaterialId)
                                .in(WaterTag::getRfidCode, rfidList)
                                .eq(WaterTag::getUseStatus, StatusConstant.YES))
                        .stream().map(WaterTag::getMaterialId)
                        .collect(Collectors.toSet()));

        return new ArrayList<>(errorMaterialIdList);
    }

    /**
     * 保存盘点结果
     */
    private Long savaCheck(List<Long> errorMaterialId) {
        CheckInfo checkInfo = new CheckInfo();
        checkInfo.setCheckResult(errorMaterialId.size() == 0 ? 1 : 0);
        checkInfo.setType(1);
        checkInfo.setStatus(0);
        checkInfo.setPeopleCheckResult(1);
        checkInfo.setStockHouseId(HouseConstant.GROUND_FLOOR_ID);
        checkInfo.setBeginTime(new Date());
        save(checkInfo);
        return checkInfo.getId();
    }

    /**
     * 保存盘点结果明细
     */
    private void savaCheckDetailList(List<Long> errorMaterialId, Long checkId) {

        // 库存信息
        List<Stock> stockList = stockService.list(q -> q.in(Stock::getMaterialId, errorMaterialId));

        // 库存信息map
        Map<Long, Stock> stockMap = stockList.stream().collect(Collectors.toMap(
                Stock::getMaterialId,
                item -> item,
                (v1, v2) -> v1
        ));

        // 盘点结果明细列表
        List<CheckDetail> checkDetailList = errorMaterialId.stream().map(materialId -> {
            CheckDetail checkDetail = new CheckDetail();
            checkDetail.setCheckId(checkId);
            checkDetail.setMaterialId(materialId);

            Stock stock = stockMap.get(materialId);
            if (stock == null) {
                checkDetail.setStockQuantity(BigDecimal.ZERO);
                checkDetail.setStockCount(0);
            } else {
                checkDetail.setStockQuantity(stock.getQuantity());
                checkDetail.setStockCount(stock.getNumber());
            }
            checkDetail.setCompleteCheck(StatusConstant.NO);
            checkDetail.setStockHouseId(HouseConstant.GROUND_FLOOR_ID);

            return checkDetail;
        }).collect(Collectors.toList());

        checkDetailService.saveBatch(checkDetailList);
    }

}
