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

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.metadata.IPage;
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.tofly.api.CodeClient;
import com.tofly.common.core.util.Date8Util;
import com.tofly.common.core.util.StringUtil;
import com.tofly.common.oauth.exception.ToflyDeniedException;
import com.tofly.common.oauth.util.SecurityUtils;
import com.tofly.watermeter.custom.entity.BwSbRkCkTwo;
import com.tofly.watermeter.custom.entity.MeterInfo;
import com.tofly.watermeter.custom.entity.SearchSb;
import com.tofly.watermeter.em.Constant;
import com.tofly.watermeter.em.ExcelExportFileTypeEnum;
import com.tofly.watermeter.entity.*;
import com.tofly.watermeter.mapper.BwrkdetailMapper;
import com.tofly.watermeter.service.*;
import com.tofly.watermeter.strategy.ExcelStrategyContext;
import com.tofly.watermeter.util.ExcelUtil;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 入库记录明细表
 *
 * @author admin
 * @date Wed Mar 10 00:00:00 CST 2021
 */
@Service("bwrkdetailService")
@AllArgsConstructor
public class BwrkdetailServiceImpl extends ServiceImpl<BwrkdetailMapper, Bwrkdetail> implements BwrkdetailService {


    private final BwrksbdatailService sbdatailService;
    private final TfYwpnBwsjblglService sjblService;
    private final BwsjdatailService sjService;
    private final CodeClient client;
    private final BwrktotalService totalService;
    private final ExcelStrategyContext excelStrategyContext;


//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public boolean insert(List<Bwrkdetail> bwrkdetailList) {
//       Date createTime = Date8Util.getCurrentDate();
////        Map<Long, Map<Long, Map<Long, Long>>> cjAndKjAndLx = getCjKjLx();
//        //这里很明确一个批次里面的种类不多，只有几种
//        for (Bwrkdetail detail : bwrkdetailList) {
//            long startNum = 0;
//            long abortNum = 0;
//            //万一前端输入的情况是截止比起止编号小，所以需要判断
//            if (detail.getBwBqh() > detail.getBwBzh()) {
//                //如果输入的起始编号大于截止编号，则反过来
//                startNum = detail.getBwBzh();
//                abortNum = detail.getBwBqh();
//            } else {
//                startNum = detail.getBwBqh();
//                abortNum = detail.getBwBzh();
//            }
//
//            String lsbh = detail.getBwLsbh();//流水编号
//            //这里的厂家和类型口径全部存的是名字
//            Long cj = detail.getBwSbcj();//厂家
//            Long lx = detail.getBwSblx();//类型
//            Long kj = detail.getBwSbkj();//口径
//
//            //这里是将前缀中如果有“-”就过滤掉，然后在末尾添加一个“-”，以便之后的方便
//            String prefix = detail.getBwBhqz();//.replaceAll("-","")+"-";//前缀
//            //存在没有前缀的情况，则取查询厂家的前缀编号
////            if (StringUtil.isEmpty(prefix)){
////                prefix = bwcjcodeMapper.getQzById(cj);
////            }
//
//
//            //插入数据之前需要判断数据库中是否已经有录入的改水表编号，如果有则不能重复录入水表编号
//            List<Bwrksbdatail> bwrksbdatailList = sbdatailService.list(Wrappers.<Bwrksbdatail>lambdaQuery()
//                    .eq(Bwrksbdatail::getBwBhqz,prefix).le(Bwrksbdatail::getRkid , abortNum).ge(Bwrksbdatail::getRkid , startNum)
//                    .select(Bwrksbdatail::getBwSbbh));
//            List<String> sbbhList = bwrksbdatailList.stream().map(Bwrksbdatail::getBwSbbh).collect(Collectors.toList());
//
//            if (sbbhList.size() > 0) {
//                throw new ToflyDeniedException("水表编号：" + JSONArray.toJSONString(sbbhList) + "，不能重复入库");
//            }
//
//            //计算多少只水表
//            long num = abortNum - startNum + 1;
//
//            //查询汇总表中的数据
//            Bwrktotal bwrktotal = totalService.getOne(Wrappers.<Bwrktotal>lambdaQuery().eq(Bwrktotal::getBwSbcj, cj).eq(Bwrktotal::getBwSbkj, kj)
//                    .eq(Bwrktotal::getBwSblx, lx).select(Bwrktotal::getId, Bwrktotal::getBwKcsl, Bwrktotal::getBwKczs, Bwrktotal::getBwSysl));
//            //如果汇总表中没有数据，则插入
//            if (StringUtil.isNull(bwrktotal)) {
//                //保存到入库汇总表中
//                Bwrktotal total = new Bwrktotal();
//                total.setBwSbcj(cj);
//                total.setBwSblx(lx);
//                total.setBwSbkj(kj);
//                total.setBwKczs(num);
//                total.setBwKcsl(num);
//                total.setBwSysl(0L);
//                totalService.save(total);
//            }//如果汇总表中有这个数据，则修改库存总数和库存数
//            else {
//                totalService.update(Wrappers.<Bwrktotal>lambdaUpdate().eq(Bwrktotal::getId,bwrktotal.getId())
//                        .set(Bwrktotal::getBwKcsl,bwrktotal.getBwKcsl()+num).set(Bwrktotal::getBwKczs,bwrktotal.getBwKczs()+num));
//            }
//            //水表入库详情表
//            detail.setBwSl(num);
//            detail.setBwBhqz(prefix);
//            detail.setCreateDate(createTime);
//            //保存到入库记录明细表
//            baseMapper.insert(detail);
//
//
//            //查询入库水表明细表的最大ID
//            long maxId = baseMapper.selectMaxId("TF_YWPN_BWRKSBDATAIL_W");
//            List<Bwrksbdatail> sbList = new ArrayList<>();
//            //利用for，将各个水表编号数据以及批量插入时的ID计算出来
//            for (int i = 0; i < num; i++) {
//                Bwrksbdatail sb = new Bwrksbdatail();
//                //水表ID
//                sb.setId(maxId + i);
//                //拼接水表编号
//                String sbBh = prefix + (startNum + i);
//                sb.setBwSbbh(sbBh);
//                sb.setRkid(startNum + i);
//                sbList.add(sb);
//
//            }
//            //水表的公共信息单独设置；如：流水编号、厂家、类型、口径等
//            Bwrksbdatail sbAll = new Bwrksbdatail();
//            sbAll.setBwLsbh(lsbh);
//            sbAll.setBwSbcj(cj);
//            sbAll.setBwSblx(lx);
//            sbAll.setBwSbkj(kj);
////            sbAll.setBwBhqz(prefix);
//            sbAll.setBwSbzt("55");
//
//            //入库时间
//            sbAll.setCreateDate(Date8Util.getCurrentDate());
//            sbAll.setCreateUser(SecurityUtils.getUserId());
//            //保存入库水表
//            sbdatailService.insertBatch(sbList, sbAll);
//
//            //根据口径查询首检比例
//            TfYwpnBwsjblgl bwcjbl = sjblService.getOne(Wrappers.<TfYwpnBwsjblgl>lambdaQuery()
//                    .eq(TfYwpnBwsjblgl::getSbkj, kj).select(TfYwpnBwsjblgl::getSjbl));
//            //为null表示该口径没有配置首检比例
//            if (StringUtil.isNull(bwcjbl)) {
//                //查询该口径的名称
//
////                Syschildcode syschildcode = new Syschildcode();
//                String kjName = client.getname(kj);
////                childCodeService.getOne(Wrappers.<Syschildcode>lambdaQuery().eq(Syschildcode::getId,kj).select(Syschildcode::getCname)).getCname();
//                throw new ToflyDeniedException("口径：" + kjName + "，没有配置首检比例，请先配置改口径的首检比例");
//            }
//            Integer ratio = bwcjbl.getSjbl();
//            //抽检个数 四舍五入
////            int ratioNum = Math.round(num * ratio / 100);
//            //抽检个数  去除
//            int ratioNum = (int) (num * ratio / 100);
//
//            //不用计算出具体抽检的水表编号
////        //随机数
////        Random random = new Random();
////        //保存的水表编号
////        Set<String> set = new HashSet();
////        //提取需要首检的水表，这里是抽检个数，
////        while (set.size() < ratioNum) {
//////利用随机数在起始编号--结束编号这个范围内，抽取到ratioNum个水表编号，用Hashset保存，避免了重复水表编号的出现，即使重复，长度也不会增加，所以使用的while循环，比较set的长度
////            long m = (random.nextInt((int) num) + startNum);//num条件是生成0~num的随机数，加个startNum就可以在startNum和abortNum区间了
////            String sbNum=prefix+m;
////            //得到了 抽检水表编号集合
////            set.add(sbNum);
////        }
//            //如果不满足1个的时候，则强行修改为1个
//            if (ratioNum == 0) {
//                ratioNum = 1;
//            }
//
//            //水表首检记录表
//            Bwsjdatail sjdetail = new Bwsjdatail();
//            sjdetail.setBwRkid(detail.getId());
//            sjdetail.setBwPcbh(lsbh);
//            sjdetail.setBwSbcj(cj);
//            sjdetail.setBwSblx(lx);
//            sjdetail.setBwSbkj(kj);
//            sjdetail.setBwSbsl(num);
//            sjdetail.setBwCjsl(ratioNum);
//            sjdetail.setBwCjbl(ratio + "%");
//            sjService.save(sjdetail);
//        }
//        return true;
//    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveTwo(BwSbRkCkTwo sbrk) {
        Date createTime = Date8Util.getCurrentDate();
        String sbbhS = sbrk.getSbbhList();
        String lsbh = sbrk.getBwLsbh();
        Long cj = sbrk.getBwSbcj();
        Long kj = sbrk.getBwSbkj();
        Long lx = sbrk.getBwSblx();
        String isSjPlan = sbrk.getIsSjPlan();
        if (StringUtil.isEmpty(sbbhS)){
            throw new ToflyDeniedException("请输入水表编号");
        }
        //本次入库水表个数
        List<String> sbbhList = Arrays.asList(sbbhS.split(","));
        Long num = Long.valueOf(sbbhList.size());

        //判断水表表号是否有重复的
        List<Bwrksbdatail> bwrksbdatailList = new ArrayList<>();
        //如果yhbhList的长度超过了1000，那么使用数据库的IN查询会报错，IN查询最大只能包含1000个数据，所以需要根据1000分段查询
        int size = sbbhList.size();
        //向上取整 , 获得次数，但是次数不多，只有几次，所以可以使用for循环分段查询
        int count = (int) Math.ceil(Double.valueOf(size) / 1000d);
        //一个区域段最多1000多条数据，所以这里最多循环两次
        for (int i = 0; i < count; i++) {
            int startIndex = 0 + i * 1000;
            int endIndex = 1000 + i * 1000;
            List<String> subShbhList = new ArrayList<>();
            //判断是否截取的最后一次数据，如果是最后一次数据，则不能使用endIndex这个参数去截取了，有可能会存在endIndex参数值超过索引最大值的情况
            if (i < count - 1) {
                //分段采取数据 0~999 ； 1000~1999 , 2000~2999 .....//这里subList方法无法截取最后一位，所以需要多写一位索引
                subShbhList = sbbhList.subList(startIndex, endIndex);
            } else {
                //分段采取最后的数据
                subShbhList = sbbhList.subList(startIndex, sbbhList.size());
            }
            //        //插入数据之前需要判断数据库中是否已经有录入的改水表编号，如果有则不能重复录入水表编号
            List<Bwrksbdatail> subBwrkList = sbdatailService.list(Wrappers.<Bwrksbdatail>lambdaQuery()
                    .in(Bwrksbdatail::getBwSbbh , subShbhList)
                    .select(Bwrksbdatail::getBwSbbh));
            bwrksbdatailList.addAll(subBwrkList);
        }

        List<String> querySbbhList = bwrksbdatailList.stream().map(Bwrksbdatail::getBwSbbh).collect(Collectors.toList());
        if (querySbbhList.size() > 0) {
            throw new ToflyDeniedException("水表编号：" + JSONArray.toJSONString(querySbbhList) + "，不能重复入库");
        }

        //查询汇总表中的数据
        Bwrktotal bwrktotal = totalService.getOne(Wrappers.<Bwrktotal>lambdaQuery().eq(Bwrktotal::getBwSbcj, cj).eq(Bwrktotal::getBwSbkj, kj)
                .eq(Bwrktotal::getBwSblx, lx).select(Bwrktotal::getId, Bwrktotal::getBwKcsl, Bwrktotal::getBwKczs, Bwrktotal::getBwSysl));
        //如果汇总表中没有数据，则插入
        if (StringUtil.isNull(bwrktotal)) {
            //保存到入库汇总表中
            Bwrktotal total = new Bwrktotal();
            total.setBwSbcj(cj);
            total.setBwSblx(lx);
            total.setBwSbkj(kj);
            total.setBwKczs(num);
            total.setBwKcsl(num);
            total.setBwSysl(0L);
            totalService.save(total);
        }//如果汇总表中有这个数据，则修改库存总数和库存数
        else {
            totalService.update(Wrappers.<Bwrktotal>lambdaUpdate().eq(Bwrktotal::getId,bwrktotal.getId())
                    .set(Bwrktotal::getBwKcsl,bwrktotal.getBwKcsl()+num).set(Bwrktotal::getBwKczs,bwrktotal.getBwKczs()+num));
        }
        //水表入库详情表
        Bwrkdetail detail = new Bwrkdetail();
        detail.setBwSl(num);
        detail.setCreateDate(createTime);
        detail.setBwSbcj(cj);
        detail.setBwSbkj(kj);
        detail.setBwSblx(lx);
        detail.setBwLsbh(lsbh);
        detail.setBwKczs(num);
        detail.setBwKcsl(num);
        detail.setBwSysl(0L);
        //保存到入库记录明细表
        baseMapper.insert(detail);

        //查询入库水表明细表的最大ID
        long maxId = baseMapper.selectMaxId("TF_YWPN_BWRKSBDATAIL_W");
        List<Bwrksbdatail> sbList = new ArrayList<>();
        //利用for，将各个水表编号数据以及批量插入时的ID计算出来
        for (int i = 0; i < sbbhList.size(); i++) {
            Bwrksbdatail sb = new Bwrksbdatail();
            //水表ID
            sb.setId(maxId + i);
            //水表编号
            String sbBh = sbbhList.get(i);
            sb.setBwSbbh(sbBh);
            sb.setRkid((long) i);
            sbList.add(sb);
        }
        //水表的公共信息单独设置；如：流水编号、厂家、类型、口径等
        Bwrksbdatail sbAll = new Bwrksbdatail();
        sbAll.setBwLsbh(lsbh);
        sbAll.setBwSbcj(cj);
        sbAll.setBwSblx(lx);
        sbAll.setBwSbkj(kj);
        sbAll.setBwSbzt("55");
        //入库时间
        sbAll.setCreateDate(Date8Util.getCurrentDate());
        sbAll.setCreateUser(SecurityUtils.getUserId());
        sbAll.setRkpcid(detail.getId());
        //保存入库水表
        sbdatailService.insertBatch(sbList, sbAll);
        //入库首检选项：需要首检
        if ("1".equals(isSjPlan)){
            //根据口径查询首检比例
            TfYwpnBwsjblgl bwcjbl = sjblService.getOne(Wrappers.<TfYwpnBwsjblgl>lambdaQuery()
                    .eq(TfYwpnBwsjblgl::getSbkj, kj).select(TfYwpnBwsjblgl::getSjbl));
            //为null表示该口径没有配置首检比例
            if (StringUtil.isNull(bwcjbl)) {
                //查询该口径的名称
                String kjName = client.getname(kj);
                throw new ToflyDeniedException("口径：" + kjName + "，没有配置首检比例，请先配置改口径的首检比例");
            }
            Integer ratio = bwcjbl.getSjbl();
            //抽检个数 四舍五入
//            int ratioNum = Math.round(num * ratio / 100);
            //抽检个数  去除
            int ratioNum = (int) (num * ratio / 100);
            //如果不满足1个的时候，则强行修改为1个
            if (ratioNum == 0) {
                ratioNum = 1;
            }
            //水表首检记录表
            Bwsjdatail sjdetail = new Bwsjdatail();
            sjdetail.setBwRkid(detail.getId());
            sjdetail.setBwPcbh(lsbh);
            sjdetail.setBwSbcj(cj);
            sjdetail.setBwSblx(lx);
            sjdetail.setBwSbkj(kj);
            sjdetail.setBwSbsl(Math.toIntExact(num));
            sjdetail.setBwCjsl((long) ratioNum);
            sjdetail.setBwCjbl(ratio + "%");
            sjService.save(sjdetail);
        }
        return true;
    }


    @Override
    public Page getSbByLsbh(String lsbh, Page page) {
        return sbdatailService.page(page, Wrappers.<Bwrksbdatail>lambdaQuery()
                .eq(StringUtil.isNotEmpty(lsbh), Bwrksbdatail::getBwLsbh, lsbh)
                );
    }

    @Override
    public void updateNum(Map<Long, Long> rkIdAndNum) {
        baseMapper.updateNum(rkIdAndNum);
    }

    @Override
    public Long getMaxId(String tableName) {
        return baseMapper.selectMaxId(tableName);
    }

    @Override
    public IPage<MeterInfo> getRkRecord(Page page , SearchSb searchSb, HttpServletResponse response) {
        String templateFlag = searchSb.getTemplateFlag();
        IPage<MeterInfo> iPage = baseMapper.getRkRecord(page , searchSb);
        //如果传的是 1，则代表导出Excel数据
        if ("1".equals(templateFlag)) {
            // 根据templateServiceType,templateFlag获取对应模板数据
            ExcelExportFileTypeEnum excelExportFileTypeEnum = ExcelExportFileTypeEnum.getExcelExportFileTypeEnum("meterBatchServiceImpl", templateFlag);
            if (null == excelExportFileTypeEnum) {
                return null;
            }
            //将查询的结果转json字符串的格式，因为导出的时候只能传一个字符串
            String resultListToString = JSONArray.toJSONStringWithDateFormat(iPage.getRecords(),"yyyy-MM-dd HH:mm:ss");
            // 从模型类型枚举根据templateServiceType获取策略对应模板数据
            List<Map<String, Object>> dataList = excelStrategyContext.getExportData("meterBatchServiceImpl", templateFlag, resultListToString);
            ExcelUtil.export(dataList.get(0), excelExportFileTypeEnum, Constant.FILE_PATH_TEST_EXPORT_EXCEL, response);
            return null;
        }
        return iPage;
    }


    private Map<Long, Map<Long, Map<Long, Long>>> getCjKjLx() {
        //入库前先查询入库总数信息，按口径，类型
        List<Bwrktotal> totalList = totalService.list();
        //最外层map的key存放的是厂家，内部再定义一个Map，存放口径和类型变量，key是口径，value中再一个map，这其中的map存储的Kay是类型，value是库存数
        Map<Long, Map<Long, Map<Long, Long>>> cjAndKjAndLx = new HashMap<>();

        //遍历三种变量（厂家、类型和口径）
        if (totalList.size() > 0) {
            totalList.forEach(k -> {
                //存放口径和map
                Map<Long, Map<Long, Long>> kjAndLx = new HashMap<>();
                //存水表类型和水表库存的ID
                Map<Long, Long> map = new HashMap<>();
                map.put(k.getBwSblx(), k.getId());
                kjAndLx.put(k.getBwSbkj(), map);
                cjAndKjAndLx.put(k.getBwSbcj(), kjAndLx);
            });
        }
        return cjAndKjAndLx;
    }

    /**
     * @param num 入库总数
     * @param kj  水表口径
     * @param lx  水表类型
     * @return 插入数据的主键ID
     */
    public Long saveRkTotal(Long num, Long cj, Long kj, Long lx) {
        //没有口径，直接库存里面添加数据
        Bwrktotal bwrktotal = new Bwrktotal();
        bwrktotal.setBwKcsl(num);
        bwrktotal.setBwKczs(num);
        bwrktotal.setBwSbkj(kj);
        bwrktotal.setBwSblx(lx);
        bwrktotal.setBwSbcj(cj);
        bwrktotal.setBwSysl(0L);
        totalService.save(bwrktotal);
        //返回刚插入数据的主键ID
        return bwrktotal.getId();
    }

}
