/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the yifu4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.yifu.cloud.plus.v1.check.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import com.alibaba.excel.util.ListUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yifu.cloud.plus.v1.check.constant.ChecksConstants;
import com.yifu.cloud.plus.v1.check.entity.TCanCheck;
import com.yifu.cloud.plus.v1.check.entity.TCheckBankNo;
import com.yifu.cloud.plus.v1.check.entity.TPreEmpCheckNum;
import com.yifu.cloud.plus.v1.check.mapper.TCheckBankNoMapper;
import com.yifu.cloud.plus.v1.check.service.TCanCheckService;
import com.yifu.cloud.plus.v1.check.service.TCheckBankNoService;
import com.yifu.cloud.plus.v1.check.service.TPreEmpCheckNumService;
import com.yifu.cloud.plus.v1.check.utils.ChecksUtil;
import com.yifu.cloud.plus.v1.check.vo.CheckBankNoThreeVo;
import com.yifu.cloud.plus.v1.check.vo.CheckBankNoVo;
import com.yifu.cloud.plus.v1.check.vo.CheckBatchVo;
import com.yifu.cloud.plus.v1.yifu.archives.entity.TEmpWorkRecording;
import com.yifu.cloud.plus.v1.yifu.archives.entity.TEmployeeInfo;
import com.yifu.cloud.plus.v1.yifu.archives.vo.EmpWorkRecordExcelVO;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.exception.ErrorCodes;
import com.yifu.cloud.plus.v1.yifu.common.core.util.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.concurrent.*;

/**
 * 银行卡卡号 校验
 *
 * @author fxj
 * @date 2022-06-20 19:56:33
 */
@RequiredArgsConstructor
@Service
public class TCheckBankNoServiceImpl extends ServiceImpl<TCheckBankNoMapper, TCheckBankNo> implements TCheckBankNoService {

	private final TCanCheckService canCheckService;
	private final TPreEmpCheckNumService tPreEmpCheckNumService;

	@Override
	public R<TCheckBankNo> checkBankNo(String name, String cardNo) {
		synchronized (this){
			TCheckBankNo  checkBankNo = baseMapper.selectOne(Wrappers.<TCheckBankNo>query().lambda()
					.eq(TCheckBankNo::getBankNo, cardNo)
					.eq(TCheckBankNo::getName, name)
					.last(CommonConstants.LAST_ONE_SQL));
			if (Common.isNotNull(checkBankNo)){
				if ("01".equals(checkBankNo.getResult())) {
					return R.ok(checkBankNo);
				} else {
					return R.failed(checkBankNo);
				}
			}
			R<TCheckBankNo> resR = ChecksUtil.checkBankNoTwo(name,cardNo,canCheckService.getCanCheck());
			if (Common.isNotNull(resR) && Common.isNotNull(resR.getData())){
				baseMapper.insert(resR.getData());
			}
			return resR;
		}
	}
	@Override
	public CheckBankNoVo checkBankNoThree(String name, String cardNo, String idNum) {
		CheckBankNoVo vo = new CheckBankNoVo();
		if (Common.isEmpty(name)  || Common.isEmpty(cardNo)){
			vo.setRes(R.failed(MsgUtils.getMessage(ErrorCodes.CHECKS_BANK_NO_REQUEST_PARAM_ERROR)));
			return vo;
		}
		synchronized (this){
			TCheckBankNo  checkBankNo = baseMapper.selectOne(Wrappers.<TCheckBankNo>query().lambda()
					.eq(TCheckBankNo::getBankNo,cardNo)
					.eq(TCheckBankNo::getName,name)
					.last(CommonConstants.LAST_ONE_SQL));
			if (Common.isNotNull(checkBankNo)){
				vo.setRes(R.ok(checkBankNo));
				return vo;
			}
			R<TCheckBankNo> resR = ChecksUtil.checkBankNoThree(name,cardNo,idNum,canCheckService.getCanCheck());
			if (Common.isNotNull(resR) && Common.isNotNull(resR.getData())){
				baseMapper.insert(resR.getData());
			}
			vo.setRes(resR);
			return vo;
		}
	}

	@Override
	public R<String> checkPreBankNo(String empName, String bankNo, String deptId) {
		if (Common.isEmpty(empName)  || Common.isEmpty(bankNo)){
			return R.failed(MsgUtils.getMessage(ErrorCodes.CHECKS_BANK_NO_REQUEST_PARAM_ERROR));
		}
		synchronized (this){
			TCheckBankNo  checkBankNo = baseMapper.selectOne(Wrappers.<TCheckBankNo>query().lambda()
					.eq(TCheckBankNo::getBankNo,bankNo)
					.eq(TCheckBankNo::getName,empName)
					.last(CommonConstants.LAST_ONE_SQL));
			if (Common.isNotNull(checkBankNo)){
				if (CommonConstants.ZERO_ONE.equals(checkBankNo.getResult())) {
					return R.ok();
				} else {
					return R.failed(checkBankNo.getRemark());
				}
			}

			boolean canCheck = canCheckService.getCanCheck();
			String nowDay = DateUtil.getThisDay();
			if (canCheck) {
				TPreEmpCheckNum nowDayNum = tPreEmpCheckNumService.getTPreEmpCheckNum(deptId, nowDay);
				if (nowDayNum == null) {
					nowDayNum = new TPreEmpCheckNum();
					nowDayNum.setCreateDay(nowDay);
					nowDayNum.setDeptId(deptId);
					nowDayNum.setCanApiNum(1000);
					nowDayNum.setApiNum(1);
					tPreEmpCheckNumService.save(nowDayNum);
				} else {
					if (nowDayNum.getApiNum() >= nowDayNum.getCanApiNum()) {
						return R.failed("该项目当日总条数：" + nowDayNum.getCanApiNum() + "已到达上限!");
					} else {
						nowDayNum.setApiNum(nowDayNum.getApiNum() + 1);
						tPreEmpCheckNumService.updateById(nowDayNum);
					}
				}
			}
			R<TCheckBankNo> resR = ChecksUtil.checkBankNoTwo(empName,bankNo,canCheckService.getCanCheck());
			if (Common.isNotNull(resR) && Common.isNotNull(resR.getData())){
				baseMapper.insert(resR.getData());
				checkBankNo = resR.getData();
				if (CommonConstants.ZERO_ONE.equals(checkBankNo.getResult())) {
					return R.ok();
				} else {
					return R.failed(checkBankNo.getRemark());
				}
			} else {
				return R.failed("校验卡号失败，校验服务未返回结果！");
			}
		}
	}

	@Override
	public CheckBankNoVo checkBankNoTwo(String name, String cardNo) {
		CheckBankNoVo vo = new CheckBankNoVo();
		if (Common.isEmpty(name)  || Common.isEmpty(cardNo)){
			vo.setRes(R.failed(MsgUtils.getMessage(ErrorCodes.CHECKS_BANK_NO_REQUEST_PARAM_ERROR)));
			return vo;
		}
		synchronized (this){
			TCheckBankNo  checkBankNo = baseMapper.selectOne(Wrappers.<TCheckBankNo>query().lambda()
					.eq(TCheckBankNo::getBankNo,cardNo)
					.eq(TCheckBankNo::getName,name)
					.last(CommonConstants.LAST_ONE_SQL));
			if (Common.isNotNull(checkBankNo)){
				vo.setRes(R.ok(checkBankNo));
				return vo;
			}
			R<TCheckBankNo> resR = ChecksUtil.checkBankNoTwo(name,cardNo,canCheckService.getCanCheck());
			if (Common.isNotNull(resR) && Common.isNotNull(resR.getData())){
				baseMapper.insert(resR.getData());
			}
			vo.setRes(resR);
			return vo;
		}
	}

	@Override
	public CheckBatchVo checkBankNoBatch(List<TCheckBankNo> list) throws ExecutionException, InterruptedException{
		CheckBatchVo vo = new CheckBatchVo();
		List<String> noList = new ArrayList<>();
		for (TCheckBankNo no : list) {
			if (Common.isNotNull(no.getBankNo())) {
				noList.add(no.getBankNo());
			}
		}
		Map<String, TCheckBankNo> noMap = new HashMap<>();
		if (!noList.isEmpty()) {
			List<TCheckBankNo> checkBankNoList = baseMapper.getListByNoList(noList);
			if (checkBankNoList != null && !checkBankNoList.isEmpty()) {
				for (TCheckBankNo no : checkBankNoList) {
					noMap.put(no.getBankNo(), no);
				}
			}
		}
		List<TCheckBankNo> noCurlist = new ArrayList<>();
		TCheckBankNo cur;
		Map<String, Boolean> bankMap = new HashMap<>();
		for (TCheckBankNo check : list) {
			cur = noMap.get(check.getBankNo());
			if (cur != null) {
				if (CommonConstants.ZERO_ONE.equals(cur.getResult())) {
					bankMap.put(cur.getBankNo(), cur.getName().equals(check.getName()));
				} else {
					if (cur.getName().equals(check.getName())) {
						bankMap.put(cur.getBankNo(), false);
					} else {
						noCurlist.add(check);
					}
				}
			} else {
				noCurlist.add(check);
			}
		}
		List<TCheckBankNo> saveList = new ArrayList<>();
		if (!noCurlist.isEmpty()) {
			boolean canCheck = canCheckService.getCanCheck();
			int taskSize = 4;
			// 创建一个线程池
			ExecutorService pool = Executors.newFixedThreadPool(taskSize);
			List<Future> listF = new ArrayList<>();
			if (noCurlist.size()>=4) {
				int l = 4;
				Callable c;
				Future f;
				for (int k=0;k<l;k++) {
					if ((k+1)==l) {
						List<TCheckBankNo> finalList = noCurlist.subList(k * (noCurlist.size() / l), noCurlist.size());
						if (canCheck) {
							c = new MyCallable(bankMap, finalList, saveList);
							// 执行任务并获取Future对象
							f = pool.submit(c);
							listF.add(f);
						} else {
							doNomal(bankMap, saveList, finalList);
						}
					} else {
						List<TCheckBankNo> finalList = noCurlist.subList(k * (noCurlist.size() / l), (k + 1) * (noCurlist.size() / l));
						if (canCheck) {
							c = new MyCallable(bankMap, finalList, saveList);
							// 执行任务并获取Future对象
							f = pool.submit(c);
							listF.add(f);
						} else {
							doNomal(bankMap, saveList, finalList);
						}
					}
				}
			} else {
				if (canCheck) {
					Callable c = new MyCallable(bankMap, noCurlist, saveList);
					// 执行任务并获取Future对象
					Future f = pool.submit(c);
					listF.add(f);
				} else {
					doNomal(bankMap, saveList, noCurlist);
				}
			}
			if (canCheck) {
				// 关闭线程池
				pool.shutdown();
				for (Future fs : listF) {
					// 从Future对象上获取任务的返回值，并输出到控制台
					fs.get();
				}
			}
			//fo (TCheckBankNo no : noCurlist)
				// 原代码
				/*resR = ChecksUtil.checkBankNoTwo(no.getName(), no.getBankNo(), canCheck)
				f (Common.isNotNull(resR) && resR.getCode() == CommonConstants.SUCCESS
						&& Common.isNotNull(resR.getData()))
					newNo = resR.getData()
					bankMap.put(no.getBankNo(), CommonConstants.ZERO_ONE.equals(newNo.getResult()))
					saveList.add(newNo)
				 else
					bankMap.put(no.getBankNo(), false)
				*/
			if (!saveList.isEmpty()) {
				this.saveOrUpdateBatch(saveList);
				saveList.clear();
			}
		}
		vo.setCheckMap(bankMap);
		return vo;
	}

	@Override
	public CheckBatchVo checkBankNoAndNameBatch(List<TCheckBankNo> list) throws ExecutionException, InterruptedException{
		CheckBatchVo vo = new CheckBatchVo();
		List<String> noList = new ArrayList<>();
		for (TCheckBankNo no : list) {
			if (Common.isNotNull(no.getBankNo())) {
				noList.add(no.getBankNo());
			}
		}
		Map<String, TCheckBankNo> noMap = new HashMap<>();
		if (!noList.isEmpty()) {
			List<TCheckBankNo> checkBankNoList = baseMapper.getListByNoList(noList);
			if (checkBankNoList != null && !checkBankNoList.isEmpty()) {
				for (TCheckBankNo no : checkBankNoList) {
					noMap.put(no.getBankNo() + CommonConstants.DOWN_LINE_STRING + no.getName(), no);
				}
			}
		}
		List<TCheckBankNo> noCurlist = new ArrayList<>();
		TCheckBankNo cur;
		Map<String, Boolean> bankMap = new HashMap<>();
		for (TCheckBankNo check : list) {
			cur = noMap.get(check.getBankNo() + CommonConstants.DOWN_LINE_STRING + check.getName());
			if (cur != null) {
				if (CommonConstants.ZERO_ONE.equals(cur.getResult())) {
					bankMap.put(cur.getBankNo() + CommonConstants.DOWN_LINE_STRING + cur.getName(), cur.getName().equals(check.getName()));
				} else {
					if (cur.getName().equals(check.getName())) {
						bankMap.put(cur.getBankNo() + CommonConstants.DOWN_LINE_STRING + cur.getName(), false);
					} else {
						noCurlist.add(check);
					}
				}
			} else {
				if (check.getBankNo().length() > 50) {
					bankMap.put(check.getBankNo() + CommonConstants.DOWN_LINE_STRING + check.getName(), false);
				} else {
					noCurlist.add(check);
				}
			}
		}
		List<TCheckBankNo> saveList = new ArrayList<>();
		if (!noCurlist.isEmpty()) {
			boolean canCheck = canCheckService.getCanCheck();
			int taskSize = 4;
			// 创建一个线程池
			ExecutorService pool = Executors.newFixedThreadPool(taskSize);
			List<Future> listF = new ArrayList<>();
			if (noCurlist.size()>=4) {
				int l = 4;
				Callable c;
				Future f;
				for (int k=0;k<l;k++) {
					if ((k+1)==l) {
						List<TCheckBankNo> finalList = noCurlist.subList(k * (noCurlist.size() / l), noCurlist.size());
						if (canCheck) {
							c = new MyCallable(bankMap, finalList, saveList);
							// 执行任务并获取Future对象
							f = pool.submit(c);
							listF.add(f);
						} else {
							doNomalByName(bankMap, saveList, finalList);
						}
					} else {
						List<TCheckBankNo> finalList = noCurlist.subList(k * (noCurlist.size() / l), (k + 1) * (noCurlist.size() / l));
						if (canCheck) {
							c = new MyCallable(bankMap, finalList, saveList);
							// 执行任务并获取Future对象
							f = pool.submit(c);
							listF.add(f);
						} else {
							doNomalByName(bankMap, saveList, finalList);
						}
					}
				}
			} else {
				if (canCheck) {
					Callable c = new MyCallable(bankMap, noCurlist, saveList);
					// 执行任务并获取Future对象
					Future f = pool.submit(c);
					listF.add(f);
				} else {
					doNomalByName(bankMap, saveList, noCurlist);
				}
			}
			if (canCheck) {
				// 关闭线程池
				pool.shutdown();
				for (Future fs : listF) {
					// 从Future对象上获取任务的返回值，并输出到控制台
					fs.get();
				}
			}
			//fo (TCheckBankNo no : noCurlist)
				// 原代码
				/*resR = ChecksUtil.checkBankNoTwo(no.getName(), no.getBankNo(), canCheck)
				f (Common.isNotNull(resR) && resR.getCode() == CommonConstants.SUCCESS
						&& Common.isNotNull(resR.getData()))
					newNo = resR.getData()
					bankMap.put(no.getBankNo(), CommonConstants.ZERO_ONE.equals(newNo.getResult()))
					saveList.add(newNo)
				 else
					bankMap.put(no.getBankNo(), false)
				*/
			if (!saveList.isEmpty()) {
				this.saveOrUpdateBatch(saveList);
				saveList.clear();
			}
		}
		vo.setCheckMap(bankMap);
		return vo;
	}

	private void doNomalByName(Map<String, Boolean> bankMap, List<TCheckBankNo> saveList, List<TCheckBankNo> finalList) {
		TCheckBankNo newNo;
		for (TCheckBankNo no : finalList) {
			newNo = new TCheckBankNo();
			newNo.setBankNo(no.getBankNo());
			newNo.setName(no.getName());
			newNo.setMessage(ChecksConstants.CHECK_CONFIG_ACCESS);
			newNo.setResult("01");
			newNo.setType(CommonConstants.ONE_STRING);
			newNo.setRemark(ChecksConstants.CHECK_CONFIG_ACCESS);
			saveList.add(newNo);
			bankMap.put(no.getBankNo() + CommonConstants.DOWN_LINE_STRING + no.getName(), true);
		}
	}

	private void doNomal(Map<String, Boolean> bankMap, List<TCheckBankNo> saveList, List<TCheckBankNo> finalList) {
		TCheckBankNo newNo;
		for (TCheckBankNo no : finalList) {
			newNo = new TCheckBankNo();
			newNo.setBankNo(no.getBankNo());
			newNo.setName(no.getName());
			newNo.setMessage(ChecksConstants.CHECK_CONFIG_ACCESS);
			newNo.setResult("01");
			newNo.setType(CommonConstants.ONE_STRING);
			newNo.setRemark(ChecksConstants.CHECK_CONFIG_ACCESS);
			saveList.add(newNo);
			bankMap.put(no.getBankNo(), true);
		}
	}

	static class MyCallable implements Callable<Object> {
		private final List<TCheckBankNo> saveList;
		private final List<TCheckBankNo> noList;
		private final Map<String, Boolean> bankMap;

		MyCallable(Map<String, Boolean> bankMap, List<TCheckBankNo> no, List<TCheckBankNo> saveList) {
			this.noList = no;
			this.bankMap = bankMap;
			this.saveList = saveList;
		}

		public Map<String, Boolean> call() {
			for (TCheckBankNo no : noList) {
				// 1.调用银行卡信息校验api
				R<TCheckBankNo> resR = ChecksUtil.checkBankNoTwoMethod(no.getName(), no.getBankNo());
				if (Common.isNotNull(resR) && resR.getCode() == CommonConstants.SUCCESS
						&& Common.isNotNull(resR.getData())) {
					TCheckBankNo newNo = resR.getData();
					bankMap.put(no.getBankNo(), CommonConstants.ZERO_ONE.equals(newNo.getResult()));
					saveList.add(newNo);
				} else {
					bankMap.put(no.getBankNo(), false);
				}
			}
			return bankMap;
		}
	}

	@Override
	public R<List<ErrorMessage>> importDiy(InputStream inputStream) {
		List<ErrorMessage> errorMessageList = new ArrayList<>();
		ExcelUtil<CheckBankNoThreeVo> util1 = new ExcelUtil<>(CheckBankNoThreeVo.class);;
		// 写法2：
		// 匿名内部类 不用额外写一个DemoDataListener
		// 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
		try {
			EasyExcel.read(inputStream, CheckBankNoThreeVo.class, new ReadListener<CheckBankNoThreeVo>() {
				/**
				 * 单次缓存的数据量
				 */
				public static final int BATCH_COUNT = CommonConstants.BATCH_COUNT;
				/**
				 *临时存储
				 */
				private List<CheckBankNoThreeVo> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

				@Override
				public void invoke(CheckBankNoThreeVo data, AnalysisContext context) {
					ReadRowHolder readRowHolder = context.readRowHolder();
					Integer rowIndex = readRowHolder.getRowIndex();
					data.setRowIndex(rowIndex+1);
					cachedDataList.add(data);
					if (cachedDataList.size() >= BATCH_COUNT) {
						saveData();
						// 存储完成清理 list
						cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
					}
				}


				@Override
				public void doAfterAllAnalysed(AnalysisContext context) {
					saveData();
				}

				/**
				 * 加上存储数据库
				 */
				private void saveData() {
					importRecord(cachedDataList,errorMessageList);
				}
			}).sheet().doRead();
		}catch (Exception e){
			log.error(CommonConstants.IMPORT_DATA_ANALYSIS_ERROR,e);
			return R.failed(CommonConstants.IMPORT_DATA_ANALYSIS_ERROR);
		}
		return  R.ok(errorMessageList);
	}

	private void importRecord(List<CheckBankNoThreeVo> cachedDataList, List<ErrorMessage> errorMessageList) {
		if (Common.isNotNull(cachedDataList)){
			CheckBankNoVo vo;
			TCheckBankNo  checkBankNo;
			List<CheckBankNoVo> res = new ArrayList<>();
			for (CheckBankNoThreeVo threeVo:cachedDataList){
				vo = new CheckBankNoVo();
				if (Common.isEmpty(threeVo.getName())  || Common.isEmpty(threeVo.getBankNo())|| Common.isEmpty(threeVo.getIdNum())){
					vo.setRes(R.failed(MsgUtils.getMessage(ErrorCodes.CHECKS_BANK_NO_REQUEST_PARAM_ERROR)));
					res.add(vo);
				}
				synchronized (this){
					checkBankNo = baseMapper.selectOne(Wrappers.<TCheckBankNo>query().lambda()
							.eq(TCheckBankNo::getBankNo,threeVo.getBankNo())
							.eq(TCheckBankNo::getName,threeVo.getName())
							.eq(TCheckBankNo::getIdNum,threeVo.getIdNum())
							.last(CommonConstants.LAST_ONE_SQL));
					if (Common.isNotNull(checkBankNo)){
						vo.setRes(R.ok(checkBankNo));
						res.add(vo);
					}
					R<TCheckBankNo> resR = ChecksUtil.checkBankNoThree(threeVo.getName(),threeVo.getBankNo(),threeVo.getIdNum(),canCheckService.getCanCheck());
					if (Common.isNotNull(resR) && Common.isNotNull(resR.getData())){
						baseMapper.insert(resR.getData());
					}
					vo.setRes(resR);
					res.add(vo);
					if (resR.getCode()==200){
						log.error(threeVo.getBankNo()+"|"
								+threeVo.getIdNum()+"|"
								+threeVo.getName()+"|"
								+resR.getData().getResult()+"|"
								+resR.getData().getRemark());
					}else {
						log.error(threeVo.getBankNo()+"|"
								+threeVo.getIdNum()+"|"
								+threeVo.getName()+"|"
								+resR.getMsg());
					}
				}
			}
		}
	}

}
