package com.quantgroup.asset.distribution.service.asset.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Stopwatch;
import com.quantgroup.asset.distribution.config.annotation.Attribute;
import com.quantgroup.asset.distribution.enums.response.AssetResponse;
import com.quantgroup.asset.distribution.exception.QGException;
import com.quantgroup.asset.distribution.exception.QGExceptionType;
import com.quantgroup.asset.distribution.model.form.AssetForm;
import com.quantgroup.asset.distribution.model.response.GlobalResponse;
import com.quantgroup.asset.distribution.service.alarm.IAlarmService;
import com.quantgroup.asset.distribution.service.asset.IAssetAttributeExtendConfigService;
import com.quantgroup.asset.distribution.service.asset.IAssetAttributeService;
import com.quantgroup.asset.distribution.service.asset.IAssetService;
import com.quantgroup.asset.distribution.service.distribute.IAssetDistributeService;
import com.quantgroup.asset.distribution.service.distribute.IDistributeFailLogService;
import com.quantgroup.asset.distribution.service.jpa.entity.Asset;
import com.quantgroup.asset.distribution.service.jpa.entity.AssetAttributeExtendConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 资产Service
 * @author liwenbin
 *
 */
@Slf4j
@Service
public class AssetServiceImpl implements IAssetService{

	@Autowired
	private IAssetAttributeExtendConfigService assetAttributeExtendConfigService;
	@Autowired
	private IAssetAttributeService assetAttributeService;
	@Autowired
	private IAssetDistributeService assetDistributeService;
	@Autowired
	private IAlarmService alarmService;
	@Autowired
	private IDistributeFailLogService distributeFailLogService;


	@Async
	@Override
	public void assetsIn(AssetForm assetForm) {
		try {
			Stopwatch stopwatch = Stopwatch.createStarted();
			// 转换为资产Object
			Asset asset = assetForm.transToAsset();
			// 获取所有资产扩展属性配置
			List<AssetAttributeExtendConfig> assetAttributeExtendConfigList = assetAttributeExtendConfigService.getAllExtendConfig();
			// 获取所有资产扩展属性值
			Map<String, Object> data = assetAttributeService.getAllAssetAttributeValue(assetAttributeExtendConfigList, assetForm);
			// 资产入库
			assetAttributeService.saveAssetAttrubite(asset, assetAttributeExtendConfigList, data);
			// 把资产基础属性值放入data
			data = addAssetAttributeToData(asset, data);
			// 资产分发
			assetDistributeService.distribute(assetForm, asset, data);
			log.info("资产分发完成, uuid : {}, bizNo : {}, assetNo : {}, bizChannel : {}, 耗时 : {}", assetForm.getUuid(),
					assetForm.getBizNo(), assetForm.getAssetNo(), assetForm.getBizChannel(), stopwatch.stop().elapsed(TimeUnit.MILLISECONDS));
		} catch (QGException qe) {
			log.error("资产入库分发出现错误 : {}, uuid : {}, bizChannel : {}, bizType : {}, bizNo : {}, assetNo : {} ",
					qe.qgExceptionType.code + "->" + qe.detail, assetForm.getUuid(),
					assetForm.getBizChannel(), assetForm.getBizType(),
					assetForm.getBizNo(), assetForm.getAssetNo());
			distributeFailLogService.saveDistributeFailLog(assetForm, qe.qgExceptionType.code + "->" + qe.detail);
			alarmService.dingtalkAlarm("Warn", "资产入库分发出现错误", "bizChannel : " + assetForm.getBizChannel()
					+ " , bizType : " + assetForm.getBizType() + " , bizNo : " + assetForm.getBizNo()
					+ " , assetNo : " + assetForm.getAssetNo() + " , uuid : " + assetForm.getUuid()
					+ " , 错误信息 : " + qe.qgExceptionType.code + "->" + qe.detail);
		} catch (Exception ex) {
			log.error("资产入库分发出现异常, uuid : {}, bizChannel : {}, bizType : {}, bizNo : {}, assetNo : {} ", assetForm.getUuid(),
					assetForm.getBizChannel(), assetForm.getBizType(),
					assetForm.getBizNo(), assetForm.getAssetNo(), ex);
			distributeFailLogService.saveDistributeFailLog(assetForm, "未知异常.");
			alarmService.dingtalkAlarm("Warn", "资产入库分发出现异常", "bizChannel : " + assetForm.getBizChannel()
					+ " , bizType : " + assetForm.getBizType() + " , bizNo : " + assetForm.getBizNo()
					+ " , assetNo : " + assetForm.getAssetNo() + " , uuid : " + assetForm.getUuid()
					+ " , 错误信息 : 未知错误.");
		}
	}

	@Override
	public GlobalResponse checkAssetForm(AssetForm assetForm) {
		// auditResult和deadLine必填
		if (StringUtils.isEmpty(assetForm.getAuditResult()) || StringUtils.isEmpty(assetForm.getDeadLine())) {
			log.info("资产入库auditResult或deadLine为空, uuid : {}, bizNo : {}", assetForm.getUuid(), assetForm.getBizNo());
			return GlobalResponse.create(AssetResponse.AUDIT_RESULT_OR_DEAD_LINE_IS_EMPTY);
		}
		// auditResult为true时, 校验是使用资方模块还是老金融产品集
		if ("true".equals(assetForm.getAuditResult())) {
			if (StringUtils.isNotEmpty(assetForm.getFinanceProducts())) {
				return checkOldAssetForm(assetForm);
			} else {
				return checkNewAssetForm(assetForm);
			}
		} else {
			return GlobalResponse.create(AssetResponse.SUCCESS);
		}
	}

	public GlobalResponse checkNewAssetForm(AssetForm assetForm) {
	    //"8"-vcc 不校验期数
		if (StringUtils.isEmpty(assetForm.getAmount()) || (StringUtils.isEmpty(assetForm.getTerm()) && !"8".equals(assetForm.getBizType())
				&& !"101".equals(assetForm.getBizType()))) {
			return GlobalResponse.create(AssetResponse.AMOUNT_OR_TERM_IS_EMPTY);
		}
		return GlobalResponse.create(AssetResponse.SUCCESS);
	}

	public GlobalResponse checkOldAssetForm(AssetForm assetForm) {
		if (StringUtils.isEmpty(assetForm.getFinanceProducts()) || StringUtils.isEmpty(assetForm.getAmount())) {
			// auditResult为true，金融产品集和amount不能为空
			log.info("资产入库auditResult为true时, 金融产品集或amount为空, uuid : {}, bizNo : {}", assetForm.getUuid(), assetForm.getBizNo());
			return GlobalResponse.create(AssetResponse.AMOUNT_OR_FINANCE_PRODUCTS_IS_EMPTY);
		}
		BigDecimal amount = new BigDecimal(assetForm.getAmount());
		BigDecimal floor = null;
		JSONArray array = JSON.parseArray(assetForm.getFinanceProducts());
		for (int i = 0, len = array.size(); i < len; ++i) {
			JSONObject data = array.getJSONObject(i);
			BigDecimal min = new BigDecimal(data.getString("min"));
			BigDecimal max = new BigDecimal(data.getString("max"));
			BigDecimal cha = max.subtract(min);
			// 0 <= max - min <= 1
			if (!(cha.compareTo(BigDecimal.ZERO) > -1 && cha.compareTo(BigDecimal.ONE) < 1)) {
				log.info("资产入库，金融产品集不符合(0 <= max - min <= 1)规则, uuid : {}, bizNo : {}", assetForm.getUuid(), assetForm.getBizNo());
				return GlobalResponse.create(AssetResponse.FINANCE_PRODUCTS_IS_ERROR1);
			}
			if (floor == null || min.compareTo(floor) < 0) {
				floor = min;
			}
		}
		// amount >= floor
		if (amount.compareTo(floor) < 0) {
			log.info("资产入库，金融产品及不符合(amount >= floor)规则, uuid : {}, bizNo : {}", assetForm.getUuid(), assetForm.getBizNo());
			return GlobalResponse.create(AssetResponse.FINANCE_PRODUCTS_IS_ERROR2);
		}
		return GlobalResponse.create(AssetResponse.SUCCESS);
	}

	/**
	 * 将资产属性值放入data
	 * @param asset
	 * @return
	 */
	public Map<String, Object> addAssetAttributeToData(Asset asset, Map<String, Object> data) {
		try {
			Class<?> clazz = asset.getClass();
			Field[] flelds = clazz.getDeclaredFields();
			for (Field field : flelds) {
				if (field.isAnnotationPresent(Attribute.class) && field.getAnnotation(Attribute.class).value()) {
					field.setAccessible(true);
					String name = transformHumpStrToUnderLineStr(field.getName());
					data.put(name, field.get(asset));
				}
			}
		} catch (Exception e) {
			log.error("资产属性值转换放入data异常, asset : {}", JSON.toJSONString(asset), e);
			throw new QGException(QGExceptionType.TRANSFORM_ASSET_ATTRIBUTE_ERROR);
		}
		return data;
	}

	/**
	 * 将驼峰字符转转换为下划线字符串
	 * @param name
	 * @return
	 */
	public String transformHumpStrToUnderLineStr(String name) {
		StringBuilder sb = new StringBuilder();
		char[] chs = name.toCharArray();
		for (int i = 0, len = chs.length; i < len; ++i) {
			if (Character.isUpperCase(chs[i])) {
				sb.append("_" + Character.toLowerCase(chs[i]));
			} else {
				sb.append(chs[i]);
			}
		}
		return sb.toString();
	}
}
