package nc.bs.arap.busireg;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import nc.bs.arap.actions.BillEffectBatchBSAction;
import nc.bs.arap.actions.BillUnEffectBatchBSAction;
import nc.bs.arap.util.BillRetiveInfoUtils;
import nc.bs.arap.util.BillSettlementUtils;
import nc.bs.businessevent.BdUpdateEvent;
import nc.bs.businessevent.BusinessEvent;
import nc.bs.businessevent.IBusinessEvent;
import nc.bs.businessevent.IBusinessListener;
import nc.bs.framework.common.NCLocator;
import nc.cmp.utils.CmpInterfaceProxy;
import nc.impl.pubapp.env.BSContext;
import nc.itf.arap.fieldmap.IBillFieldGet;
import nc.itf.arap.pub.IArapBillService;
import nc.itf.cmp.settlement.ISettlement;
import nc.md.persist.framework.MDPersistenceService;
import nc.pubitf.accperiod.AccountCalendar;
import nc.pubitf.org.IOrgUnitPubService;
import nc.vo.arap.basebill.BaseAggVO;
import nc.vo.arap.basebill.BaseBillVO;
import nc.vo.arap.basebill.BaseItemVO;
import nc.vo.arap.event.IArapBSEventType;
import nc.vo.arap.pub.BillEnumCollection;
import nc.vo.arap.pub.BillEnumCollection.BillSatus;
import nc.vo.arap.pub.BillEnumCollection.InureSign;
import nc.vo.arap.utils.ARAPModuleEnableUtil;
import nc.vo.cmp.settlement.CmpMsg;
import nc.vo.cmp.settlement.SettlementAggVO;
import nc.vo.cmp.settlement.SettlementBodyVO;
import nc.vo.cmp.settlement.SettlementHeadVO;
import nc.vo.pub.AggregatedValueObject;
import nc.vo.pub.BusinessException;
import nc.vo.pub.CircularlyAccessibleValueObject;
import nc.vo.pub.lang.UFBoolean;
import nc.vo.pub.lang.UFDate;
import nc.vo.pub.lang.UFDouble;
import nccloud.bs.arap.sagas.util.SagasUtils;
import nccloud.commons.lang.ArrayUtils;

/**
 * 现金结算信息接口
 * @author liaobx at 2011-3-24
 */
public class NotifySettleHandleListener implements IBusinessListener {
	// 单据 - 现金结算 : 暂存, 新增, 更新, 删除, 审批, 反审批
	private static final List<String> EVENTTYPE = Arrays.asList(new String[] { IArapBSEventType.TYPE_TEMPSAVE_AFTER, // 暂存
			IArapBSEventType.TYPE_INSERT_AFTER, // 新增后
			IArapBSEventType.TYPE_UPDATE_AFTER, // 修改后
			IArapBSEventType.TYPE_DELETE_AFTER, // 删除后
			IArapBSEventType.TYPE_APPROV_AFTER, // 核准后
			IArapBSEventType.TYPE_UNAPPROV_BEFORE // 取消核准前

	});

	protected AggregatedValueObject[] getOriginBill(AggregatedValueObject[] bills) throws BusinessException {
		String[] pks = new String[bills.length];
		for (int index = 0; index < pks.length; index++) {
			pks[index] = bills[index].getParentVO().getPrimaryKey();
		}
		Collection<?> vo = MDPersistenceService.lookupPersistenceQueryService().queryBillOfVOByPKs(bills[0].getClass(), pks, false);
		Map<String, AggregatedValueObject> map = new HashMap<String, AggregatedValueObject>();
		List<BaseItemVO> rets = new ArrayList<BaseItemVO>();
		for (Object object : vo) {
			AggregatedValueObject bill = (AggregatedValueObject) object;
			for (CircularlyAccessibleValueObject item : bill.getChildrenVO()) {
				if (!(Integer.valueOf(1).equals(item.getAttributeValue("dr")))) {
					rets.add((BaseItemVO) item);
				}
			}
			bill.setChildrenVO(rets.toArray(new BaseItemVO[] {}));
			rets.clear();
			map.put(bill.getParentVO().getPrimaryKey(), bill);
		}
		// 按照pk是重新排序
		List<AggregatedValueObject> list = new ArrayList<AggregatedValueObject>();
		for (int i = 0; i < pks.length; i++) {
			list.add(map.get(pks[i]));
		}
		return list.toArray(new BaseAggVO[] {});
	}

	public void doAction(IBusinessEvent event) throws BusinessException {
		AggregatedValueObject[] bills = this.getBills(event);
		// 日志监控，定位偶发性问题 —— 2021年7月1日 cyz
		nc.bs.logging.Log log = nc.bs.logging.Log.getInstance("ncm_log");// ncm_log是日志名
		log.debug("---------------------nc.bs.arap.busireg.NotifySettleHandleListener.doAction(IBusinessEvent)----------------------- ");
		StringBuilder logMsg = new StringBuilder();

		String eventType = event.getEventType();
		if (ArrayUtils.isEmpty(bills) || !EVENTTYPE.contains(eventType)) {
			return;
		}

		UFBoolean isinit = ((BaseAggVO) bills[0]).getHeadVO().getIsinit();
		// 期初单据不处理
		if (isinit != null && isinit.booleanValue()) {
			return;
		}
		// 转单回写判断
		UFBoolean transferWriteBack = (UFBoolean) BSContext.getInstance().getSession("transferWriteBack");
		if (transferWriteBack != null && transferWriteBack.booleanValue()) {
			return;
		}

		boolean isCmpEnable = ARAPModuleEnableUtil.isCMPEnable((String) bills[0].getParentVO().getAttributeValue(IBillFieldGet.PK_GROUP));
		
		// 日志监控
		logMsg.setLength(0);
		logMsg
		.append("billno：").append(bills[0].getParentVO().getAttributeValue(IBillFieldGet.BILLNO))
		.append(" 是否安装：").append(isCmpEnable);
		log.debug(logMsg.toString());
		
		List<AggregatedValueObject> settAggvos = new ArrayList<AggregatedValueObject>();
		List<AggregatedValueObject> nosettAggvos = new ArrayList<AggregatedValueObject>();
		List<AggregatedValueObject> editOldSettVOs = new ArrayList<AggregatedValueObject>();
		List<AggregatedValueObject> editOldNoSettVOs = new ArrayList<AggregatedValueObject>();

		if (isCmpEnable) {
			IOrgUnitPubService orgUnitPubService = NCLocator.getInstance().lookup(IOrgUnitPubService.class);
			String createdrt = null;
			try {
				String org = (String) bills[0].getParentVO().getAttributeValue(IBillFieldGet.PK_ORG);
				// 根据组织、模块取得组织期初期间
				createdrt = orgUnitPubService.getOrgModulePeriodByOrgIDAndModuleID(org, ARAPModuleEnableUtil.CMP_MODULE_ID);
				if (createdrt != null) {
					String initEnabledYear = createdrt.substring(0, 4); /* 启用年 */
					String initEnabledMonth = createdrt.substring(5, 7); /* 启用月 */
					AccountCalendar instanceByPkOrg = AccountCalendar.getInstanceByPk_org(org);
					instanceByPkOrg.set(initEnabledYear, initEnabledMonth);
					UFDate createDate2 = instanceByPkOrg.getMonthVO().getBegindate();

					if (createDate2.after((UFDate) bills[0].getParentVO().getAttributeValue(IBillFieldGet.BILLDATE))) {
						isCmpEnable = false;
						
						// 日志监控
						logMsg.setLength(0);
						logMsg
						.append("billno：").append(bills[0].getParentVO().getAttributeValue(IBillFieldGet.BILLNO))
						.append(" 启用日期在单据日期之后！");
						log.debug(logMsg.toString());
					}
				} else {
					throw new BusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("2006v61020_0", "02006v61020-0119")/* "现金管理已在集团安装，必须启用期初期间!" */);
				}
			} catch (BusinessException e) {
				isCmpEnable = false;
				
				// 日志监控
				logMsg.setLength(0);
				logMsg
				.append("billno：").append(bills[0].getParentVO().getAttributeValue(IBillFieldGet.BILLNO))
				.append(" 出现异常：").append(e.getMessage());
				log.debug(logMsg.toString());
			}

			// modify by pangjsh 启用资金后之前未传结算的收付单据，不走结算处理，防止未触发生效/反生效事件
			if (IArapBSEventType.TYPE_UNAPPROV_BEFORE.equals(eventType)) {
				Map<String, AggregatedValueObject> billsMap = new HashMap<String, AggregatedValueObject>();
				List<AggregatedValueObject> nosettlebills = new ArrayList<AggregatedValueObject>();
				List<AggregatedValueObject> settlebills = new ArrayList<AggregatedValueObject>();
				for (AggregatedValueObject bill : bills) {
					billsMap.put(bill.getParentVO().getPrimaryKey(), bill);
				}
				SettlementAggVO[] aggVOs = CmpInterfaceProxy.INSTANCE.getQueryService().querySettlementAggsByPk_busibills(billsMap.keySet().toArray(new String[0]));
				if (aggVOs == null || aggVOs.length == 0) {
					nosettlebills.addAll(Arrays.asList(bills));
				} else {
					for (SettlementAggVO settlementAggVO : aggVOs) {
						SettlementHeadVO headVO = (SettlementHeadVO) settlementAggVO.getParentVO();
						if (billsMap.keySet().contains(headVO.getPk_busibill())) {
							settlebills.add(billsMap.get(headVO.getPk_busibill()));
						} else {
							nosettlebills.add(billsMap.get(headVO.getPk_busibill()));
						}
					}
				}
				if (nosettlebills.size() > 0)
					settVOsWithoutCMP(nosettlebills.toArray(new AggregatedValueObject[0]), eventType);
				if (settlebills.size() == 0)
					return;
				else
					bills = settlebills.toArray(new AggregatedValueObject[0]);
			} else if (IArapBSEventType.TYPE_APPROV_AFTER.equals(eventType)) {
				Map<String, AggregatedValueObject> billsMap = new HashMap<String, AggregatedValueObject>();
				Map<String, AggregatedValueObject> originbillsMap = new HashMap<String, AggregatedValueObject>();
				List<AggregatedValueObject> nosettlebills = new ArrayList<AggregatedValueObject>();
				List<AggregatedValueObject> settlebills = new ArrayList<AggregatedValueObject>();
				// 获取数据库中原始单据的生效状态而查询数据
				AggregatedValueObject[] orginBills = this.getOriginBill(bills);
				for (AggregatedValueObject bill : bills) {
					billsMap.put(bill.getParentVO().getPrimaryKey(), bill);
				}
				for (AggregatedValueObject bill : orginBills) {
					originbillsMap.put(bill.getParentVO().getPrimaryKey(), bill);
				}
				// 未审批而单据仍已生效，由于之前bug产生的数据要特殊处理，未审批已生效单据按照启用资金模块处理，防止重复生成busidata数据
				SettlementAggVO[] aggVOs = CmpInterfaceProxy.INSTANCE.getQueryService().querySettlementAggsByPk_busibills(billsMap.keySet().toArray(new String[0]));
				if (aggVOs == null || aggVOs.length == 0) {
					for (AggregatedValueObject bill : bills) {
						BaseBillVO originBill = (BaseBillVO) originbillsMap.get(bill.getParentVO().getPrimaryKey()).getParentVO();
						UFBoolean isrefund = (UFBoolean) originBill.getAttributeValue(IBillFieldGet.ISREFUND);
						UFDouble money = (UFDouble) originBill.getAttributeValue(IBillFieldGet.MONEY);
						String pk_billtype = (String) originBill.getAttributeValue(IBillFieldGet.PK_BILLTYPE);
						if (isrefund != null && isrefund.booleanValue() && money.compareTo(UFDouble.ZERO_DBL) < 0 && (IBillFieldGet.F0.equals(pk_billtype) || IBillFieldGet.F2.equals(pk_billtype))) {
							nosettlebills.add(bill);
						} else if (originBill.getEffectstatus() != BillEnumCollection.InureSign.OKINURE.VALUE) {
							nosettlebills.add(bill);
						} else {
							settlebills.add(bill);
						}
					}
				} else {
					for (SettlementAggVO settlementAggVO : aggVOs) {
						SettlementHeadVO headVO = (SettlementHeadVO) settlementAggVO.getParentVO();
						BaseBillVO originBill = (BaseBillVO) originbillsMap.get(headVO.getPk_busibill()).getParentVO();
						UFBoolean isrefund = (UFBoolean) originBill.getAttributeValue(IBillFieldGet.ISREFUND);
						UFDouble money = (UFDouble) originBill.getAttributeValue(IBillFieldGet.MONEY);
						String pk_billtype = (String) originBill.getAttributeValue(IBillFieldGet.PK_BILLTYPE);
						if (isrefund != null && isrefund.booleanValue() && money.compareTo(UFDouble.ZERO_DBL) < 0 && (IBillFieldGet.F0.equals(pk_billtype) || IBillFieldGet.F2.equals(pk_billtype))) {
							nosettlebills.add(billsMap.get(headVO.getPk_busibill()));
						} else if (billsMap.keySet().contains(headVO.getPk_busibill()) || originBill.getEffectstatus() == BillEnumCollection.InureSign.OKINURE.VALUE) {
							settlebills.add(billsMap.get(headVO.getPk_busibill()));
						} else {
							nosettlebills.add(billsMap.get(headVO.getPk_busibill()));
						}
					}
				}
				if (nosettlebills.size() > 0)
					settVOsWithoutCMP(nosettlebills.toArray(new AggregatedValueObject[0]), eventType);
				if (settlebills.size() == 0)
					return;
				else
					bills = settlebills.toArray(new AggregatedValueObject[0]);
			}

			for (AggregatedValueObject bill : bills) {
				if (!isSettleBill(bill)) {
					nosettAggvos.add(bill);
				} else {
					settAggvos.add(bill);
				}
			}
			if (IArapBSEventType.TYPE_UPDATE_AFTER.equals(eventType)) {
				for (AggregatedValueObject bill : getOrigBills(event)) {
					if (IArapBSEventType.TYPE_UPDATE_AFTER.equals(eventType)) {
						if (!isSettleBill(bill)) {
							editOldNoSettVOs.add(bill);
						} else {
							editOldSettVOs.add(bill);
						}
					}
				}
			}
		}

		if (!isCmpEnable) {
			
			// 日志监控
			logMsg.setLength(0);
			logMsg
			.append("billno：").append(bills[0].getParentVO().getAttributeValue(IBillFieldGet.BILLNO))
			.append(" 是否安装2：").append(isCmpEnable);
			log.debug(logMsg.toString());
			
			settVOsWithoutCMP(bills, eventType);
			return;
		} else {
			if (nosettAggvos.size() != 0) {
				settVOsWithoutCMP(nosettAggvos.toArray(new BaseAggVO[] {}), eventType);
			}
		}

		// 补偿场景跳过资金逻辑
		if (SagasUtils.isCompensateStage()) {
			return;
		}

		ISettlement settleServ = CmpInterfaceProxy.INSTANCE.getSettlement();
		if (IArapBSEventType.TYPE_UPDATE_AFTER.equals(eventType)) {
			if (settAggvos.size() == 0 && editOldSettVOs.size() == 0) { // 修改前后的vo都不需要传结算
				return;
			} else if (settAggvos.size() != 0) {// 修改后需要传结算，修改前是需要传结算走同样的逻辑
				if (((BaseAggVO) settAggvos.get(0)).getHeadVO().getBillstatus().intValue() == BillEnumCollection.BillSatus.Save.VALUE.intValue()) {// 单据状态是保存态
					settleServ.notifySettlementBatchSave(BillSettlementUtils.settleInfo4Insert(settAggvos.toArray(new BaseAggVO[] {})));
				} else {
					settleServ.notifySettlementBatchTempSave(BillSettlementUtils.settleInfo4Insert(settAggvos.toArray(new BaseAggVO[] {})));
				}
			} else if (settAggvos.size() == 0 && editOldSettVOs.size() != 0) {// 修改后不需要传结算，修改前需要传结算
				settleServ.notifySettlementBatchDelete(getCmpMsgs(editOldSettVOs.toArray(new AggregatedValueObject[0])));
			}
		}

		bills = settAggvos.toArray(new BaseAggVO[] {});
		if (bills.length == 0) {

			return;
		}

		if (IArapBSEventType.TYPE_TEMPSAVE_AFTER.equals(eventType)) {
			settleServ.notifySettlementBatchTempSave(BillSettlementUtils.settleInfo4Insert(bills));
		} else if (IArapBSEventType.TYPE_INSERT_AFTER.equals(eventType)) {
			
			// 日志监控
			logMsg.setLength(0);
			logMsg
			.append("billno：").append(bills[0].getParentVO().getAttributeValue(IBillFieldGet.BILLNO))
			.append(" IArapBSEventType.TYPE_INSERT_AFTER：保存后，调结算接口！");
			log.debug(logMsg.toString());
						
			settleServ.notifySettlementBatchSave(BillSettlementUtils.settleInfo4Insert(bills));
		} else if (IArapBSEventType.TYPE_APPROV_AFTER.equals(eventType)) {
			settleServ.notifySettlementBatchAudit(getCmpMsgs(bills));
			new BillRetiveInfoUtils().retryInfos(bills);
		} else if (IArapBSEventType.TYPE_DELETE_AFTER.equals(eventType)) {

			List<AggregatedValueObject> fromSettleInfos = new ArrayList<AggregatedValueObject>();
			List<AggregatedValueObject> vos = new ArrayList<AggregatedValueObject>();
			List<AggregatedValueObject> saveToTemporailyVOs = new ArrayList<AggregatedValueObject>();
			for (AggregatedValueObject bill : bills) {
				UFBoolean isfromindependentObj = ((BaseAggVO) bill).getHeadVO().getIsfromindependent();
				UFBoolean saveToTemporailyObj = ((BaseAggVO) bill).getHeadVO().getSaveToTemporarily();
				boolean saveToTemporaily = saveToTemporailyObj != null && saveToTemporailyObj.booleanValue();
				boolean isfromindependent = isfromindependentObj != null && isfromindependentObj.booleanValue();

				if (isfromindependent && ((saveToTemporailyObj == null) || (saveToTemporailyObj != null && !saveToTemporaily))) {
					fromSettleInfos.add(bill);
				}
				if (saveToTemporaily) {
					saveToTemporailyVOs.add(bill);
				} else {
					vos.add(bill);
				}
			}
			if (fromSettleInfos.size() > 0) {
				settleServ.notifySettlementBatchUnAssociate(getCmpMsgs(fromSettleInfos.toArray(new AggregatedValueObject[0])));
			} else if (vos.size() > 0) {
				settleServ.notifySettlementBatchDelete(getCmpMsgs(vos.toArray(new AggregatedValueObject[0])));
			}
			if (saveToTemporailyVOs.size() > 0) {
				settleServ.notifySettlementBatchTempSave(BillSettlementUtils.settleInfo4Insert(bills));
			}
		} else if (IArapBSEventType.TYPE_UNAPPROV_BEFORE.equals(eventType)) {
			settleServ.notifySettlementBatchReserveAudit(getCmpMsgs(bills));
			String[] pks = new String[bills.length];
			for (int i = 0; i < bills.length; i++) {
				pks[i] = bills[i].getParentVO().getPrimaryKey();
			}

			// 资金状态变更，单据支持取消审批后自动收回。故需要做完上一步后，需要传结算以变修改结算的单据状态。单据资金相关字段状态已发生变化，需要重新查询 modify by zhaoyanf
			BaseAggVO[] newAggVOs = NCLocator.getInstance().lookup(IArapBillService.class).queryArapBillByPKs(pks, (String) bills[0].getParentVO().getAttributeValue(IBillFieldGet.PK_BILLTYPE));

			settleServ.notifySettlementBatchSave(BillSettlementUtils.settleInfo4Insert(newAggVOs));

			new BillRetiveInfoUtils().retryInfos(bills);
		}

	}

	private boolean isSettleBill(AggregatedValueObject bill) {
		UFBoolean isrefund = (UFBoolean) bill.getParentVO().getAttributeValue(IBillFieldGet.ISREFUND);
		UFDouble money = (UFDouble) bill.getParentVO().getAttributeValue(IBillFieldGet.MONEY);
		String pk_billtype = (String) bill.getParentVO().getAttributeValue(IBillFieldGet.PK_BILLTYPE);
		if (isrefund != null && isrefund.booleanValue() && money.compareTo(UFDouble.ZERO_DBL) < 0 && (IBillFieldGet.F0.equals(pk_billtype) || IBillFieldGet.F2.equals(pk_billtype))) {
			return false;
		}
		SettlementBodyVO metavo = new SettlementBodyVO();
		List<SettlementBodyVO> metasList = new ArrayList<SettlementBodyVO>();
		BaseItemVO[] items = (BaseItemVO[]) bill.getChildrenVO();
		for (BaseItemVO item : items) {
			if (item.getIsdiscount() == null || UFBoolean.FALSE.equals(item.getIsdiscount())) {
				metasList.add(metavo);
			}
		}
		return !metasList.isEmpty();
	}

	private void settVOsWithoutCMP(AggregatedValueObject[] bills, String eventType) throws BusinessException {
		if (IArapBSEventType.TYPE_APPROV_AFTER.equals(eventType)) {
			// 不装现金平台 : 单据生效
			for (AggregatedValueObject vo : bills) {
				BaseBillVO parent = (BaseBillVO) vo.getParentVO();
				parent.setBillstatus(BillSatus.Sign.VALUE);
				parent.setEffectstatus(BillEnumCollection.InureSign.OKINURE.VALUE);
				parent.setEffectuser(parent.getApprover());
				parent.setEffectdate(parent.getApprovedate().getDate());
				parent.setSigndate(parent.getApprovedate().getDate());
				parent.setSignuser(parent.getApprover());

			}
			// 补偿场景跳过自动生效
			if (!SagasUtils.isCompensateStage()) {
				new BillEffectBatchBSAction().updateVO(bills);
			}
		} else if (IArapBSEventType.TYPE_UNAPPROV_BEFORE.equals(eventType)) {
			// 审批 -> 生效 逆向流程: 先反生效 -> 反审批
			for (AggregatedValueObject vo : bills) {
				BaseBillVO parent = (BaseBillVO) vo.getParentVO();
				parent.setBillstatus(BillSatus.Audit.VALUE);
				parent.setEffectstatus(InureSign.NOINURE.VALUE);
				// deleted by lvhj,这段挪到BillUnEffectBatchBSAction中的doBusiness方法中统一处理
				// 避免取消生效前事件处理时，拿不到effectdate
				// parent.setEffectuser(null);
				// parent.setEffectdate(null);
				// end delete by lvhj
				parent.setSigndate(null);
				parent.setSignuser(null);
			}
			// 补偿场景跳过自动反生效
			if (!SagasUtils.isCompensateStage()) {
				new BillUnEffectBatchBSAction().updateVO(bills);
			}
		}
	}

	/**
	 * @param bills
	 */
	private CmpMsg[] getCmpMsgs(AggregatedValueObject[] bills) {
		CmpMsg[] msgs = new CmpMsg[bills.length];
		for (int i = 0; i < bills.length; i++) {
			msgs[i] = BillSettlementUtils.settleInfo4Query(bills[i]);
		}

		return msgs;
	}

	protected AggregatedValueObject[] getBills(final IBusinessEvent event) {
		Object object = null;
		if (event instanceof BusinessEvent) {
			BusinessEvent e = (BusinessEvent) event;
			object = e.getObject();
		} else if (event instanceof BdUpdateEvent) {
			BdUpdateEvent e = (BdUpdateEvent) event;
			object = e.getNewObject();
		}

		AggregatedValueObject[] bills = null;
		if (null != object) {
			if (object.getClass().isArray()) {
				bills = (AggregatedValueObject[]) object;
			} else {
				bills = new AggregatedValueObject[1];
				bills[0] = (AggregatedValueObject) object;
			}
		}
		return bills;
	}

	protected AggregatedValueObject[] getOrigBills(final IBusinessEvent event) {
		Object object = null;
		if (event instanceof BdUpdateEvent) {
			BdUpdateEvent e = (BdUpdateEvent) event;
			object = e.getOldObject();
		} else {
			return null;
		}

		AggregatedValueObject[] bills = null;
		if (null != object) {
			if (object.getClass().isArray()) {
				bills = (AggregatedValueObject[]) object;
			} else {
				bills = new AggregatedValueObject[1];
				bills[0] = (AggregatedValueObject) object;
			}
		}
		return bills;
	}

}