/*
 * Author: wangjian
 * 生成ServiceImpl
 */
package com.tofly.watermeter.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tofly.common.core.util.Date8Util;
import com.tofly.common.core.util.DateUtil;
import com.tofly.common.core.util.StringUtil;
import com.tofly.common.oauth.exception.ToflyDeniedException;
import com.tofly.watermeter.custom.entity.BwSbRkCkTwo;
import com.tofly.watermeter.entity.*;
import com.tofly.watermeter.mapper.BwckdatailMapper;
import com.tofly.watermeter.service.*;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 水表出库批次记录表
 *
 * @author admin
 * @date Wed Mar 10 00:00:00 CST 2021
 */
@Service("bwckdatailService")
@AllArgsConstructor
public class BwckdatailServiceImpl extends ServiceImpl<BwckdatailMapper, Bwckdatail> implements BwckdatailService {

    private final BwrktotalService totalService;
    private final BwgdandckService gdAndCkService;
    private final BwrksbdatailService bwrksbdatailService;
    private final BwrkdetailService bwrkdetailService;

//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public boolean insertCk(List<Bwckdatail> ckList) {
//        ckList.forEach(k -> {
//            //            if(StringUtil.isEmpty(k.getBwBhqz())){
////                k.setBwBhqz(bwcjcodeMapper.getQzById(k.getBwSbcj()));
////            }
//
//
//            //出库数量
//            Long ckSl = k.getLqSl();
//
////            //查询输入水表判断k.getBwSbqh();
////            List<Bwrksbdatail> bwrksbdatailList = bwrksbdatailService.list(Wrappers.<Bwrksbdatail>lambdaQuery()
////                    .eq(Bwrksbdatail::getBwBhqz, k.getBwBhqz()).le(Bwrksbdatail::getRkid, k.getBwSbzh())
////                    .ge(Bwrksbdatail::getRkid, k.getBwSbqh()).select(Bwrksbdatail::getBwSbbh));
////            if (StringUtil.isNull(bwrksbdatailList)) {
////                throw new ToflyDeniedException("请正确输入水表起号、止号");
////            }
//
//
//            //先查询库存中的记录
//            Bwrktotal totalList = totalService.getOne(Wrappers.<Bwrktotal>lambdaQuery().eq(Bwrktotal::getBwSblx, k.getBwSblx())
//                    .eq(Bwrktotal::getBwSbkj, k.getBwSbkj()).eq(Bwrktotal::getBwSbcj, k.getBwSbcj())
//                    .select(Bwrktotal::getBwKcsl, Bwrktotal::getBwSysl, Bwrktotal::getBwKczs, Bwrktotal::getId));
//            //得到库存数量
//            long kcsl = totalList.getBwKcsl();
//
//            if (ckSl > kcsl) {
//                throw new ToflyDeniedException("请正确输入出库数量，不得多于库存数量");
//            } else {
//                if (StringUtil.isNull(k.getBwLqry()) || StringUtil.isNull(k.getBwLqbm())) {
//                    throw new ToflyDeniedException("请正确输入领取人员或领取部门");
//                }
//                //直接根据厂家、类型、口径查询入库批次有哪些
////                List<Bwrkdetail> bwrkdetailList = bwrkdetailService.list(Wrappers.<Bwrkdetail>lambdaQuery().eq(Bwrkdetail::getBwSbcj , k.getBwSbcj())
////                        .eq(Bwrkdetail::getBwSblx , k.getBwSblx()).eq(Bwrkdetail::getBwSbkj , k.getBwSbkj()).orderByAsc(Bwrkdetail::getId));
//                Page page = new Page();
//                page.setSize(ckSl);//只查询需要出库的水表范围；并且保证先入库的水表先出库
//                List<Bwrksbdatail> cksbList = bwrksbdatailService.page(page, Wrappers.<Bwrksbdatail>lambdaQuery().eq(Bwrksbdatail::getBwSbzt, "55")
//                        .eq(Bwrksbdatail::getBwSbcj, k.getBwSbcj()).eq(Bwrksbdatail::getBwSblx, k.getBwSblx()).eq(Bwrksbdatail::getBwSbkj, k.getBwSbkj())
//                        .orderByAsc(Bwrksbdatail::getId).select(Bwrksbdatail::getBwSbbh, Bwrksbdatail::getBwBhqz, Bwrksbdatail::getRkid)).getRecords();
//                List<String> sbbhList = cksbList.stream().map(Bwrksbdatail::getBwSbbh).collect(Collectors.toList());
//
//                k.setBwSl(ckSl);
//                Random r = new Random();
//                //流水编号
//                k.setBwLsbh(DateUtil.parseDateToStr("yyyyMMddhhmmss", Date8Util.getCurrentDate()) + System.currentTimeMillis() + r.nextInt());
//                //保存出库数据记录
//                baseMapper.insert(k);
//
//                long sysl = totalList.getBwSysl();
//                if (StringUtil.isNull(sysl)) {
//                    sysl = 0L;
//                }
//                //直接对一条库存数据修改，因为后期计算都是同一计算的，无法对所有库存记录修改，只能对其中一条修改库存数据，即使修改后这条数据为负数，但是最后的库存数量是所有口径、类型相同的库存计算结果
//                totalService.update(Wrappers.<Bwrktotal>lambdaUpdate().eq(Bwrktotal::getId, totalList.getId())
//                        .set(Bwrktotal::getBwKcsl, totalList.getBwKcsl() - ckSl).set(Bwrktotal::getBwSysl, sysl + ckSl));
//                //修改工单节点为工单归档
////                baseMapper.updateGdZt(k.getGdId(), 43);
//                //保存工单与出库ID的记录信息
////                Bwgdandck bwgdandck = new Bwgdandck();
////                bwgdandck.setSkid(k.getId());
////                bwgdandck.setGdid(k.getGdId());
////                gdAndCkService.save(bwgdandck);
//
//
//                //修改水表出库状态
////                Long sbqh = k.getBwSbqh();
////                Long sbzh = k.getBwSbzh();
////                String sbqz = k.getBwBhqz();
//                //修改水表为出库状态
//                bwrksbdatailService.update(Wrappers.<Bwrksbdatail>lambdaUpdate().in(Bwrksbdatail::getBwSbbh, sbbhList)
//                        .set(Bwrksbdatail::getBwSbzt, "56").set(Bwrksbdatail::getCkId, k.getId()));
//
//
//            }
//        });
//        return true;
//    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertCkTwo(BwSbRkCkTwo sbck) {
        Long cj = sbck.getBwSbcj();
        Long lx = sbck.getBwSblx();
        Long kj = sbck.getBwSbkj();
        Long getDept = sbck.getDeptId();
        Long getUser = sbck.getUserId();
        String sbbhS = sbck.getSbbhList().trim();
        String gdbh = sbck.getGdbh();
        if (StringUtil.isEmpty(sbbhS)) {
            throw new ToflyDeniedException("请传入水表编号");
        }
        //本次出库水表个数
        List<String> sbbhList = Arrays.asList(sbbhS.split(","));
        //出库数量
        Long ckSl = Long.valueOf(sbbhList.size());
        //先查询库存中的记录
        Bwrktotal totalList = totalService.getOne(Wrappers.<Bwrktotal>lambdaQuery().eq(Bwrktotal::getBwSblx, lx)
                .eq(Bwrktotal::getBwSbkj, kj).eq(Bwrktotal::getBwSbcj, cj)
                .select(Bwrktotal::getBwKcsl, Bwrktotal::getBwSysl, Bwrktotal::getBwKczs, Bwrktotal::getId));
        if (StringUtil.isNotNull(totalList)){
            //得到库存数量
            long kcsl = totalList.getBwKcsl();
            if (ckSl > kcsl) {
                throw new ToflyDeniedException("库存不足");
            } else {
                if (StringUtil.isNull(getUser) || StringUtil.isNull(getDept)) {
                    throw new ToflyDeniedException("请正确输入领取人员或领取部门");
                }
            }
            //入库、退库状态的，都可以出库
            String sbState = "55,62";
            //保证先入先出原则
            //但是同一批次内的水表又不遵循先入先出原则；是批次间保证先入先出原则。一个字形容：难
            //1- 根据入库批次详情表(新增保存库存数)；查询该厂家、类型、口径、库存数大于0 下顺序排序的批次记录；然后挨个批次比较，如果不够就拿下一个批次记录数据；直到拿到满足本次出库数的批次记录表
            //2- 然后根据拿到需要出库的批次记录ID；然后去入库详情水表数据库表中查询满足入库ID(需要加一个字段入库批次ID存放)，只要本次出库水表与查询出来的水表有不匹配的情况，则提示先进先出原则
            List<Bwrkdetail> bwrkdetailList = bwrkdetailService.list(Wrappers.<Bwrkdetail>lambdaQuery()
                    .eq(Bwrkdetail::getBwSbcj, cj).eq(Bwrkdetail::getBwSblx, lx).eq(Bwrkdetail::getBwSbkj, kj)
                    .ge(Bwrkdetail::getBwKcsl , 1)
                    .orderByAsc(Bwrkdetail::getCreateDate)
                    .select(Bwrkdetail::getId , Bwrkdetail::getBwLsbh , Bwrkdetail::getBwKcsl , Bwrkdetail::getBwKczs , Bwrkdetail::getBwSysl));
            //符合要求的出库批次数量
            Long allPcKcsl = 0L;
            //存放最后一批次水表出库多少只，修改批次中的水表库存时使用
            Long lastPcCksl = ckSl;
            //入库批次ID集合；只存储符合本次出库的批次数据
            List<Long> rkIdList = new ArrayList<>();
            for (int i=0 ; i<bwrkdetailList.size() ; i++){
                Bwrkdetail bwrkdetail = bwrkdetailList.get(i);
                //该批次剩余库存
                Long pcKcsl = bwrkdetail.getBwKcsl();
                allPcKcsl = allPcKcsl + pcKcsl;
                if (allPcKcsl.compareTo(ckSl) == -1){
                    //最后剩余需要出库数量
                    lastPcCksl = lastPcCksl - pcKcsl;
                    rkIdList.add(bwrkdetail.getId());
                    //如果在这一步修改的批次库存数量，则代表该批次全部出库完了，所以直接修改为 0 就完事了
                    bwrkdetailService.update(Wrappers.<Bwrkdetail>lambdaUpdate().eq(Bwrkdetail::getId , bwrkdetail.getId())
                            .set(Bwrkdetail::getBwKcsl , 0).set(Bwrkdetail::getBwSysl , bwrkdetail.getBwKczs()));
                }else {
                    //最后一次也要保存进来
                    rkIdList.add(bwrkdetail.getId());
                    //修改最后批次库存数量 ，这里修改批次库存数量，代表库存数量还足够
                    bwrkdetailService.update(Wrappers.<Bwrkdetail>lambdaUpdate().eq(Bwrkdetail::getId , bwrkdetail.getId())
                            .set(Bwrkdetail::getBwKcsl , pcKcsl - lastPcCksl).set(Bwrkdetail::getBwSysl , bwrkdetail.getBwSysl() + lastPcCksl));
                    //如果已经不小于出库数量了，则表示已经筛选出了哪些批次可以出库了；所以直接终止
                    break;
                }
            }
            // 只查询需要出库的水表范围；并且保证先入库的水表先出库
//            List<Bwrksbdatail> cksbList = bwrksbdatailService.list(Wrappers.<Bwrksbdatail>lambdaQuery().in(Bwrksbdatail::getBwSbzt, Arrays.asList(sbState.split(",")))
//                    .eq(Bwrksbdatail::getBwSbcj, cj).eq(Bwrksbdatail::getBwSblx, lx).eq(Bwrksbdatail::getBwSbkj, kj).apply("rownum < " + (ckSl + 1))
//                    .orderByAsc(Bwrksbdatail::getId).select(Bwrksbdatail::getBwSbbh));
            List<Bwrksbdatail> cksbList = bwrksbdatailService.list(Wrappers.<Bwrksbdatail>lambdaQuery()
                    .in(Bwrksbdatail::getBwSbzt, Arrays.asList(sbState.split(",")))
                    .in(Bwrksbdatail::getRkpcid , rkIdList)
//                    .eq(Bwrksbdatail::getBwSbcj, cj).eq(Bwrksbdatail::getBwSblx, lx).eq(Bwrksbdatail::getBwSbkj, kj)
                    .select(Bwrksbdatail::getBwSbbh));
            //根据先入先出的原则，下面是获得只能出库的水表，然后判断本次出库水表与下面的水表编号对比，如果存在没有对应起来的，则代表输入的出库水表编号有误，可能没有遵循先入先出原则或者水表厂家、类型、口径不对应
            List<String> rkSbbhList = cksbList.stream().map(Bwrksbdatail::getBwSbbh).collect(Collectors.toList());
            Map<String, String> sbMap = rkSbbhList.stream().collect(Collectors.toMap(String::valueOf, String::valueOf));
            //过滤没有的水表，代表是存在错误水表编号
            List<String> filterSbbhList = sbbhList.stream().filter(k -> !sbMap.containsKey(k)).collect(Collectors.toList());
            if (filterSbbhList.size() > 0) {
//                throw new ToflyDeniedException("出库时保证先入先出原则，水表编号：" + JSONArray.toJSONString(filterSbbhList) + "，请仔细检查后出库。");
                throw new ToflyDeniedException("水表编号：" + JSONArray.toJSONString(filterSbbhList) + "请仔细检查后出库");
            }
            //保存在出库详情表中
            Bwckdatail bwckdatail = new Bwckdatail();
            bwckdatail.setBwLqry(getUser);
            bwckdatail.setBwLqbm(getDept);
            bwckdatail.setBwSbkj(kj);
            bwckdatail.setBwSblx(lx);
            bwckdatail.setBwSbcj(cj);
            bwckdatail.setBwSl(ckSl);
            Random r = new Random();
            //流水编号
            bwckdatail.setBwLsbh(DateUtil.parseDateToStr("yyyyMMddhhmmss", Date8Util.getCurrentDate()) + System.currentTimeMillis() + r.nextInt());
            //保存出库数据记录
            baseMapper.insert(bwckdatail);
            //使用数量；更新库存信息
            Long sysl = totalList.getBwSysl();
            if (StringUtil.isNull(sysl)) {
                sysl = 0L;
            }
            //更新库存
            totalService.update(Wrappers.<Bwrktotal>lambdaUpdate().eq(Bwrktotal::getId, totalList.getId())
                    .set(Bwrktotal::getBwKcsl, totalList.getBwKcsl() - ckSl).set(Bwrktotal::getBwSysl, sysl + ckSl));
            //修改工单节点为工单归档
            baseMapper.updateGdZt(gdbh,"2");
            //保存工单与出库ID的记录信息
            Bwgdandck bwgdandck = new Bwgdandck();
            bwgdandck.setSkid(bwckdatail.getId());
            bwgdandck.setGdid(gdbh);
            gdAndCkService.save(bwgdandck);
            //修改水表为出库状态
            //如果长度超过了1000，那么使用数据库的IN查询会报错，IN查询最大只能包含1000个数据，所以需要根据1000分段查询
            int size = sbbhList.size();
            //向上取整 , 获得次数，但是次数不多，只有几次，所以可以使用for循环分段查询
            int count = (int) Math.ceil((double) size / 1000d);
            for (int i = 0; i < count; i++) {
                int startIndex = i * 1000;
                int endIndex = 1000 + i * 1000;
                List<String> subYhbhList;
                //判断是否截取的最后一次数据，如果是最后一次数据，则不能使用endIndex这个参数去截取了，有可能会存在endIndex参数值超过索引最大值的情况
                if (i < count - 1) {
                    //分段采取数据 0~999 ； 1000~1999 , 2000~2999 .....//这里subList方法无法截取最后一位，所以需要多写一位索引
                    subYhbhList = sbbhList.subList(startIndex, endIndex);
                } else {
                    //分段采取最后的数据
                    subYhbhList = sbbhList.subList(startIndex, sbbhList.size());
                }
                bwrksbdatailService.update(Wrappers.<Bwrksbdatail>lambdaUpdate().in(Bwrksbdatail::getBwSbbh, subYhbhList)
                        .set(Bwrksbdatail::getBwSbzt, "56").set(Bwrksbdatail::getCkId, bwckdatail.getId()));
            }
        }else {
            throw new ToflyDeniedException("无法出库；请检查水表厂家、类型、口径是否正确");
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean appCk(String sbbh) {
        //查找该水表编号的水表厂家、口径、类型，修改库存状态
        Bwrksbdatail bwrksbdatail = bwrksbdatailService.getOne(Wrappers.<Bwrksbdatail>lambdaQuery()
                .eq(Bwrksbdatail::getBwSbbh, sbbh).select(Bwrksbdatail::getBwSbcj, Bwrksbdatail::getBwSbkj,
                        Bwrksbdatail::getBwSblx, Bwrksbdatail::getBwSbzt));
        if ("56".equals(bwrksbdatail.getBwSbzt())) {
            throw new ToflyDeniedException("该水表已经出库了，不得重复");
        }
        Long cj = bwrksbdatail.getBwSbcj();
        Long kj = bwrksbdatail.getBwSbkj();
        Long lx = bwrksbdatail.getBwSblx();
        //先查询库存中的记录
        Bwrktotal total = totalService.getOne(Wrappers.<Bwrktotal>lambdaQuery().eq(Bwrktotal::getBwSblx, lx)
                .eq(Bwrktotal::getBwSbkj, kj).eq(Bwrktotal::getBwSbcj, cj)
                .select(Bwrktotal::getBwKcsl, Bwrktotal::getBwSysl, Bwrktotal::getBwKczs, Bwrktotal::getId));
        //得到库存数量
        long kcsl = total.getBwKcsl();
        //出库数量
        long ckSl = 1;
        if (ckSl > kcsl) {
            throw new ToflyDeniedException("库存数不足");
        }
        bwrksbdatailService.update(Wrappers.<Bwrksbdatail>lambdaUpdate()
                .eq(Bwrksbdatail::getBwSbbh, sbbh).set(Bwrksbdatail::getBwSbzt, "56"));
        return true;
    }

    @Override
    public String ckPrefix(Long cjId) {
        return baseMapper.ckPrefix(cjId);
    }

//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public boolean updateCk(List<BwSbCk> sbCkList) {
//        //保存入库信息的ID和出库数量情况
//        Map<Long, Long> rkIdAndNum = new HashMap<>();
//        //这里也是for循环里面操作了数据库，后期优化
//        sbCkList.forEach(c -> {
//            if (c.getNum() < 0) {
//                throw new ToflyDeniedException("出库数量不能为负数");
//            }
//            //先比较目前的出库数量是比原来的多还是少，然后判断是增加还是减少
//            Long beforeNum = baseMapper.selectOne(Wrappers.<Bwckdatail>lambdaQuery().eq(Bwckdatail::getId, c.getCkId()).select(Bwckdatail::getBwSl)).getBwSl();
//            //差值，如果是正值则表示增加的出库数量，反之亦然
//            Long differNum = c.getNum() - beforeNum;
//            //查询入库ID
//            Long rkId = baseMapper.selectOne(Wrappers.<Bwckdatail>lambdaQuery().eq(Bwckdatail::getId, c.getCkId()).select(Bwckdatail::getBwRkid)).getBwRkid();
//            if (differNum > 0) {
//                //查询现在库存数量
//                Long kcsl = rkService.getOne(Wrappers.<Bwrkdetail>lambdaQuery().eq(Bwrkdetail::getId, rkId).select(Bwrkdetail::getBwKcsl)).getBwKcsl();
//                //比较，如果当前继续增加的出库数量超过了现在的库存数量，则报错
//                if (kcsl < differNum) {
//                    //这里提示具体那个型号厂家口径的水表超过了库存数量
//                    throw new ToflyDeniedException("已超过库存数量");
//                }
//            }
//            rkIdAndNum.put(rkId, differNum);
//        });
//        //修改出库信息
//        baseMapper.updateCk(sbCkList);
//        //修改入库记录信息中的库存数量、使用数量，这里有个bug，如果同时对同一个入库数据进行操作的时候，上面的计算就会出现错误，但是业务没有出现对同一入库的水表进行操作
//        rkService.updateNum(rkIdAndNum);
//        return true;
//    }

    @Override
    public void updateGdZt(String gdbh,String val) {
        baseMapper.updateGdZt(gdbh,val);
    }

    @Override
    public String getGdbh(Long gdId) {
        return baseMapper.getGdbh(gdId);
    }


}
