package cn.ibizlab.core.extensions.service;

import cn.ibizlab.core.extensions.domain.BaseRequest;
import cn.ibizlab.core.lite.extensions.domain.ModelObj;
import cn.ibizlab.core.rule.domain.ExecLog;
import cn.ibizlab.core.rule.domain.ExecResult;
import cn.ibizlab.core.rule.service.IExecLogService;
import cn.ibizlab.core.rule.service.IExecResultService;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.builder.Message;
import org.kie.api.definition.KiePackage;
import org.kie.api.definition.rule.Rule;
import org.kie.api.io.Resource;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;

/**
 *  规则服务对象接口
 */
@Service
@Primary
@Slf4j
public class BaseEntityServiceImpl implements BaseEntityService
{

	public static final String Tag_EngineId = "engineId";

	public BaseEntityServiceImpl()
	{
		System.setProperty("drools.dateformat", "yyyy/MM/dd HH:mm:ss");
		KieServices ks = KieServices.Factory.get();
		KieContainer kContainer = ks.getKieClasspathContainer();
	}


	public void saveResult(ModelObj param, String RULECODE, String RULENAME, String RU_EXECRESULTNAME, Object BUSINESSCAT, Integer RETVALUE,
			Object KEYVALUEFIELD, Object DOMAINSFIELD,Object DIMFIELD, Object METRICFIELD, Object TIMEFIELD, Object EXT1FIELD, Object EXT2FIELD)
	{
		ExecResult result=new ExecResult();
		result.setRuleId(RULECODE);
		result.setRuleName(RULENAME);
		result.setName(RU_EXECRESULTNAME);
		result.setRetValue(RETVALUE);
		if(BUSINESSCAT!=null && BUSINESSCAT instanceof String){
			result.setBusinessCat(String.valueOf(BUSINESSCAT));
		}
		if(KEYVALUEFIELD==null)
		{
			result.setKeyValueField(param.getRowKey());
		}
		else
		{
			result.setKeyValueField(KEYVALUEFIELD.toString());
		}

		if(METRICFIELD!=null) {
			Object val = METRICFIELD;
			if ( (val instanceof BigDecimal))
				result.setMetricField(((BigDecimal) val));
			else if ( (val instanceof BigInteger))
				result.setMetricField(new BigDecimal((BigInteger) val));
			else if ( (val instanceof Double))
				result.setMetricField(new BigDecimal((Double) val));
			else if ( (val instanceof Float))
				result.setMetricField(new BigDecimal(((Float) val).doubleValue()));
			else if ( (val instanceof Long))
				result.setMetricField(new BigDecimal((Long) val));
			else if ( (val instanceof Integer))
				result.setMetricField(new BigDecimal((int)val));
			else
				result.setMetricField(new BigDecimal(1));
		}
		else
			result.setMetricField(new BigDecimal(1));
		if(DOMAINSFIELD!=null)
		{
			result.setDomainsField(DOMAINSFIELD.toString());
		}
		else
		{
			result.setDomainsField(result.getKeyValueField());
		}
		if(TIMEFIELD!=null&&TIMEFIELD instanceof Timestamp)
			result.setTimeField((Timestamp)TIMEFIELD);
		if(EXT1FIELD!=null)
			result.setExt1Field(EXT1FIELD.toString());
		if(EXT2FIELD!=null)
			result.setExt2Field(EXT2FIELD.toString());
		result.set(Tag_EngineId,param.get(Tag_EngineId));
		if(param.containsKey(RuleEngineExService.Setting_ResultDataSource)){
			result.set(RuleEngineExService.Setting_ResultDataSource, param.get(RuleEngineExService.Setting_ResultDataSource));
		}
		if(param.containsKey(RuleEngineExService.Setting_ResultTableName)){
			result.set(RuleEngineExService.Setting_ResultTableName, param.get(RuleEngineExService.Setting_ResultTableName));
		}

		if(DIMFIELD == null){
			return;
		}
		List dims = new ArrayList();
		if(DIMFIELD instanceof List){
			dims = (List)DIMFIELD;
		}else if(DIMFIELD instanceof String){
			dims.add(DIMFIELD);
		}
		for(Object obj : dims){
			if(StringUtils.isEmpty(obj))
				continue;
			result.setDimField(String.valueOf(obj));
			result.setId(result.getDefaultKey(true).toString());
			if(result.getRetValue()==1)
			{
				ruExecResultService.saveAsync(result);
			}
			else
			{
				ruExecResultService.deleteAsync(result);
			}
		}
	}

	@Autowired
	private IExecResultService ruExecResultService;

	protected Hashtable<String, KieSession> setkieSession=new Hashtable<String,KieSession>();
	protected Hashtable<String,KieContainer> setKc=new Hashtable<String,KieContainer>();
	protected Hashtable<String,Integer> setRuleCount=new Hashtable<String,Integer>();

	public void printFactsMessageCount(KieSession kieSession)
	{
		Collection allHandles = kieSession.getFactHandles();

		String msg = new StringBuilder().append("\nAll facts count:").append(allHandles.size()).append("\n").toString();
		log.debug(msg);
	}

	public void printFactsMessage(KieSession kieSession)
	{
		Collection<FactHandle> allHandles = kieSession.getFactHandles();

		String msg = "\nAll facts:\n";
		for (FactHandle handle : allHandles)
			msg = new StringBuilder().append(msg).append("    ").append(kieSession.getObject(handle)).append("\n").toString();

		log.debug(msg);
	}

	public void DeleteFactsMessage(KieSession kieSession)
	{
		Collection<FactHandle> allHandles = kieSession.getFactHandles();

		String msg = "\nAll facts:\n";
		for (FactHandle handle : allHandles)
			kieSession.delete(handle);
	}

	public static String prettyBuildMessage(Message msg)
	{
		return new StringBuilder().append("Message: {id=").append(msg.getId()).append(", level=").append(msg.getLevel()).append(", path=").append(msg.getPath()).append(", line=").append(msg.getLine())
				.append(", column=").append(msg.getColumn()).append(", text=\"").append(msg.getText()).append("\"}").toString();
	}

	public static String knowledgeBaseDetails(KieBase kbase)
	{
		if (kbase == null)
			return "Knowledge Base is null.";

		StringBuilder sb = new StringBuilder("Knowledge base built from the following packages:");

		Collection<KiePackage> packages = kbase.getKiePackages();
		for (KiePackage kp : packages)
		{
			sb.append(new StringBuilder().append("\n    Package: [").append(kp.getName()).append("]").toString());
			for (Rule rule : kp.getRules())
				sb.append(new StringBuilder().append("\n        Rule: [").append(rule.getName()).append("]").toString());
		}

		return sb.toString();
	}


	public void initCommonData()
	{
		try
		{

		}
		catch (Exception ex)
		{
			log.error("baseservice:initcommondata:" + ex.getMessage());
		}
	}


	@Value("${ibiz.rulepath:}")
	private String drlpath;


	public void reset(String keyC)
	{
		if(setKc.containsKey(keyC))
		{
			try
			{
				setKc.get(keyC).dispose();
			}
			catch (Exception ex){}

			setKc.remove(keyC);
		}
	}
	public String initRule(List<String> rulefilelist,String keyC)
	{
		String strRt="";
		try
		{
			initCommonData();
			String strResId= "RES"+ IdWorker.getIdStr();
			KieContainer kContainer=null;
			if(setKc.containsKey(keyC))
			{
				kContainer=setKc.get(keyC);
			}

			else{
				KieServices ks = KieServices.Factory.get();
				KieRepository kr = ks.getRepository();

				KieFileSystem kfs = ks.newKieFileSystem();

				for (String rulefile : rulefilelist)
				{

					log.debug(new StringBuilder().append("init rule file: rules/").append(rulefile).toString());
					//Resource rs = ks.getResources().newClassPathResource(new StringBuilder().append("rules/").append(rulefile).toString());
					String path=(new StringBuilder().append(rulefile).toString());
					Resource rs = ks.getResources().newFileSystemResource(path.replace("\\","/"),"UTF-8");



					kfs.write(rs);
				}

				KieBuilder kb = ks.newKieBuilder(kfs);
				kb.buildAll();
				if (kb.getResults().hasMessages(new Message.Level[] { Message.Level.ERROR }))
				{
					List<Message> errors = kb.getResults().getMessages(new Message.Level[] { Message.Level.ERROR });
					StringBuilder sb = new StringBuilder("Errors:");
					for (Message msg : errors)
					{
						sb.append(new StringBuilder().append("\n  ").append(prettyBuildMessage(msg)).toString());
					}

					log.error(new StringBuilder().append("build rule failed:").append(sb.toString()).toString());
					return "";
				}

				log.info(new StringBuilder().append("KieServices built: ").append(toString()).toString());

				kContainer = ks.newKieContainer(kr.getDefaultReleaseId());
				setKc.put(keyC, kContainer);
			}


			KieSession kieSession = kContainer.newKieSession();

			kieSession.setGlobal("BaseService", this);


			setkieSession.put(strResId,kieSession);
			setRuleCount.put(strResId,rulefilelist.size());
			strRt=strResId;

		}
		catch (Exception e)
		{
			log.error(e.toString());

			return "";
		}

		return strRt;
	}



	public void processRule(ModelObj entity, String strCurKeyValue,String strResId) throws Exception
	{
		if(!setkieSession.containsKey(strResId))
		{
			log.error("Service规则容器查找失败：" + strResId);
			throw new Exception("Service规则容器查找失败：" + strResId);
		}
		KieSession kieSession=setkieSession.get(strResId);

		int rulescount=setRuleCount.get(strResId);
		FactHandle fireHandle = kieSession.insert(entity);


		int rulesFired = 0;

		printFactsMessageCount(kieSession);

		// for (int iloop = 0; iloop < this.rulescount; ++iloop)
		// {
		try
		{
			rulesFired = kieSession.fireAllRules(rulescount);
		}
		catch (Exception e)
		{
			log.error("Service规则执行错误：" + e.toString());

			throw e;
		}
		finally
		{
			try
			{
				kieSession.delete(fireHandle);
			}
			catch (Exception ex){}




		}
		// }
		log.info(new StringBuilder().append("Service，Process ").append(rulesFired).append(" rules for record.\n").toString());



	}


	@Autowired
	protected IExecLogService ruExecLogService;
	public ExecLog processAll(BaseRequest msg)
	{

		ExecLog execlog=new ExecLog();
		execlog.setId(msg.getId());
		execlog.setName(msg.getBatch());
		execlog.setKeyValueField(msg.getModel());
		execlog.setRunBody(msg.toString());
		execlog.setSystemId(msg.getSystemid());
		ruExecLogService.create(execlog);

		StringBuilder strErrorInfo=new StringBuilder();
		int count = 0;
		int errorcount = 0;

		String strResId=initRule(msg.getRules(),msg.getBatch());
		if (StringUtils.isEmpty(strResId))
		{
			strErrorInfo.append("准备rule文件错误");
			strErrorInfo.append("\r\n");
			execlog.setRetCode(2);
			execlog.setCnt(count);
			execlog.setSucc(errorcount);
			execlog.setRunResult(strErrorInfo.toString());
			ruExecLogService.update(execlog);
			return execlog;
		}

		try
		{


				for (ModelObj modelObj : msg.getDatas())
				{
					try
					{

						count++;
						modelObj.set("BATCH",msg.getBatch());
						modelObj.set(Tag_EngineId, msg.getEngineId());
						if(!StringUtils.isEmpty(msg.getResultDataSource())){
							modelObj.set(RuleEngineExService.Setting_ResultDataSource, msg.getResultDataSource());
						}
						if(!StringUtils.isEmpty(msg.getResultTableName())){
							modelObj.set(RuleEngineExService.Setting_ResultTableName, msg.getResultTableName());
						}
						processRule(modelObj, modelObj.getRowKey(),strResId);

					}
					catch (Exception e)
					{
						errorcount++;
						log.error("Service错误，加载数据详细信息：" + modelObj.getRowKey() + "," + e.getMessage());
						if (strErrorInfo.toString().length() <= 5000)
						{
							strErrorInfo.append("加载数据详细信息：" + modelObj.getRowKey() + ",错误：" + e.getMessage());
							strErrorInfo.append("\r\n");
							if (e.getCause() != null && e.getCause().getMessage() != null)
							{
								strErrorInfo.append(e.getCause().getMessage());
								strErrorInfo.append("\r\n");
							}

						}

					}

				}






		}
		catch(Exception e)
		{
			log.error("Service读取BaseRequest错误:" + e.toString());
			strErrorInfo.append("读取BaseRequest错误:" + e.getMessage());
			strErrorInfo.append("\r\n");
			if (e.getCause() != null && e.getCause().getMessage() != null)
			{
				strErrorInfo.append(e.getCause().getMessage());
				strErrorInfo.append("\r\n");
			}


			execlog.setRetCode(1);
			execlog.setCnt(count);
			if (errorcount == 0)
				execlog.setSucc(0);
			else
				execlog.setSucc(count-errorcount);
			execlog.setRunResult(strErrorInfo.toString());
			ruExecLogService.update(execlog);
			return execlog;
		}
		finally
		{
			if(setkieSession.containsKey(strResId))
			{
				KieSession kieSession=setkieSession.get(strResId);
				try
				{
					kieSession.dispose();
				}
				catch (Exception ex){}

				setkieSession.remove(strResId);
				setRuleCount.remove(strResId);
			}

		}

		log.info(new StringBuilder().append("Service成功，process successfully:").append(msg.toString()).toString());
		strErrorInfo.append("Service成功，process successfully\r\n");





		execlog.setCnt(count);
		execlog.setSucc(count-errorcount);
		if(execlog.getCnt()==0)
			execlog.setRetCode(3);
		else
			execlog.setRetCode(0);
		execlog.setRunResult(strErrorInfo.toString());
		this.ruExecLogService.update(execlog);
		return execlog;
	}
 }