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

import java.math.BigDecimal;
import java.util.Map;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.quantgroup.asset.distribution.constant.RuleConstants;
import com.quantgroup.asset.distribution.enums.UnionType;
import com.quantgroup.asset.distribution.model.entity.fund.ChannelFundConfigNew;
import com.quantgroup.asset.distribution.service.rule.vo.BaseRuleVO;
import com.quantgroup.asset.distribution.service.rule.vo.IRuleVO;
import com.quantgroup.asset.distribution.service.rule.vo.UnionRuleVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.quantgroup.asset.distribution.exception.QGException;
import com.quantgroup.asset.distribution.exception.QGExceptionType;
import com.quantgroup.asset.distribution.exception.QGPreconditions;
import com.quantgroup.asset.distribution.service.rule.IRuleService;
import com.quantgroup.asset.distribution.util.calc.Expression;
import com.quantgroup.asset.distribution.util.calc.Expression.ExpressionException;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

/**
 * 规则判断
 * @author liwenbin
 *
 */
@Slf4j
@Service
public class RuleServiceImpl implements IRuleService{

	@Override
	public boolean valid(String expression, Map<String, Object> data) {
		try {
			// 如果el表达式为空，返回true
			if (StringUtils.isEmpty(expression)) { return true; }
			Expression ex = new Expression(expression);
			for (Map.Entry<String, Object> entry : data.entrySet()) {
				ex.with(entry.getKey(), String.valueOf(entry.getValue()));
			}
			BigDecimal res = ex.eval();
			QGPreconditions.checkArgument(res.compareTo(BigDecimal.ZERO) == 0 || res.compareTo(BigDecimal.ONE) == 0, QGExceptionType.RULE_CALC_UNKNOW_RESULT, expression);
			return res.compareTo(BigDecimal.ONE) == 0;
		} catch (ExpressionException expressionException) {
			log.error("规则判断出现错误, expression : {}, data : {}", expression, JSON.toJSONString(data), expressionException);
			throw new QGException(QGExceptionType.RULE_CALC_ERROR, expression);
		} catch (Exception e) {
			log.error("规则判断出现未知异常, expression : {}, data : {}", expression, JSON.toJSONString(data), e);
			throw new QGException(QGExceptionType.RULE_CALC_UNKNOW_ERROR, expression);
		}
	}

	@Override
	public boolean validNew(ChannelFundConfigNew.Limits limits, Map<String, Object> data) {
		try {
			if (StringUtils.isEmpty(limits.getLimitDetail())) { throw new QGException(QGExceptionType.RULE_EXPRESSION_IS_EMPTY); }
			IRuleVO ruleVO = translateFromWebFrontJSON(limits.getLimitDetail());
			return ruleVO.valid(data);
		} catch (QGException qx) {
			log.error("规则判断出现错误, limitDetail : {}, limitTranslate : {}, data : {}", limits.getLimitDetail(), limits.getLimitTranslate(), data);
			throw qx;
		} catch (Exception e) {
			log.error("规则判断出现未知异常, limitDetail : {}, limitTranslate : {}, data : {}", limits.getLimitDetail(), limits.getLimitTranslate(), data);
			throw new QGException(QGExceptionType.RULE_CALC_ERROR, limits.getLimitTranslate());
		}
	}

	@Override
	public IRuleVO getIRuleVo(ChannelFundConfigNew.Limits limits) {
		if (StringUtils.isEmpty(limits.getLimitDetail())) { throw new QGException(QGExceptionType.RULE_EXPRESSION_IS_EMPTY); }
		IRuleVO ruleVO = translateFromWebFrontJSON(limits.getLimitDetail());
		return ruleVO;
	}

	/**
	 * JSON转IRuleVO
	 *
	 * @param json
	 * @return
	 */
	private IRuleVO translateFromWebFrontJSON(String json) {
		try {
			JSONObject jsonObject = JSON.parseObject(json);
			if (!isUnionRule(jsonObject))
				return jsonObject.toJavaObject(BaseRuleVO.class);
			return translateUnionRuleVO(jsonObject);
		} catch (Exception e) {
			log.error("解析规则json出错,错误内容为 : {}", json, e);
			throw new QGException(QGExceptionType.RULE_EXPRESSION_IS_EMPTY);
		}
	}

	/**
	 * 检测是否组合规则
	 *
	 * @param jsonObject
	 * @return
	 */
	private boolean isUnionRule(JSONObject jsonObject) {
		QGPreconditions.checkArgument(jsonObject != null, QGExceptionType.RULE_IS_NOT_JSON);
		if (jsonObject.containsKey(RuleConstants.CONDITION)) {
			checkUnionRule(jsonObject);
			return true;
		}
		checkBaseRule(jsonObject);
		return false;
	}

	/**
	 * JSON转UnionRuleVO
	 *
	 * @param jsonObject
	 * @return
	 */
	private UnionRuleVO translateUnionRuleVO(JSONObject jsonObject) {
		JSONArray jsonArray = jsonObject.getJSONArray(RuleConstants.CONDITION);
		UnionRuleVO unionRuleVO = new UnionRuleVO(UnionType.fromCode(jsonObject.getString(RuleConstants.OPERATOR)));
		for (int i = 0; i < jsonArray.size(); i++) {
			JSONObject child = jsonArray.getJSONObject(i);
			if (!isUnionRule(child)) {
				unionRuleVO.getBaseRules().add(child.toJavaObject(BaseRuleVO.class));
			} else {
				unionRuleVO.getUnionRules().add(translateUnionRuleVO(child));
			}
		}
		return unionRuleVO;
	}

	private void checkUnionRule(JSONObject jsonObject) {
		QGPreconditions.checkArgument(jsonObject.containsKey(RuleConstants.OPERATOR) && jsonObject.size() >= 2, QGExceptionType.RULE_IS_NOT_JSON);
		QGPreconditions.checkArgument(!CollectionUtils.isEmpty(jsonObject.getJSONArray(RuleConstants.CONDITION)), QGExceptionType.RULE_IS_NOT_JSON);
		QGPreconditions.checkArgument(StringUtils.equalsAnyIgnoreCase(jsonObject.getString(RuleConstants.OPERATOR), RuleConstants.AND, RuleConstants.OR), QGExceptionType.RULE_IS_NOT_JSON);
	}

	private void checkBaseRule(JSONObject jsonObject) {
		QGPreconditions.checkArgument(jsonObject.containsKey(RuleConstants.OPERATOR) && jsonObject.size() >= 3, QGExceptionType.RULE_IS_NOT_JSON);
		QGPreconditions.checkArgument(jsonObject.containsKey(RuleConstants.KEY), QGExceptionType.RULE_IS_NOT_JSON);
		QGPreconditions.checkArgument(jsonObject.containsKey(RuleConstants.VAL), QGExceptionType.RULE_IS_NOT_JSON);
	}
}
