package com.yeejoin.amos.fas.business.service.impl;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.typroject.tyboot.core.foundation.context.SpringContextHelper;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yeejoin.amos.fas.business.action.CustomerAction;
import com.yeejoin.amos.fas.business.action.model.ContingencyRo;
import com.yeejoin.amos.fas.business.action.mq.WebMqttComponent;
import com.yeejoin.amos.fas.business.action.result.ActionResult;
import com.yeejoin.amos.fas.business.action.result.SafteyPlanResult;
import com.yeejoin.amos.fas.business.action.result.message.AbstractActionResultMessage;
import com.yeejoin.amos.fas.business.dao.mapper.RuleRuningSnapshotMapper;
import com.yeejoin.amos.fas.business.dao.repository.IRuleRuningSnapshotDao;
import com.yeejoin.amos.fas.business.service.intfc.IRuleRunningSnapshotService;
import com.yeejoin.amos.fas.business.service.model.RuleRuningSnapshot;
import com.yeejoin.amos.fas.business.service.model.ToipResponse;

/**
 * 
 * <pre>
 * 三维节点与规则消息实体视图service实现
 * </pre>
 *
 * @author amos
 * @version $Id: NodeMsgViewServiceImpl.java, v 0.1 2018年11月28日 下午6:14:51 amos Exp
 *          $
 */
@Service("ruleRunningSnapshotService")
public class RuleRunigSnapshotServiceImpl 
		implements IRuleRunningSnapshotService
		//,ExecuteMethodHook
{

	//IRuleRuningSnapshotRepository repository;
	@Autowired
	private RuleRuningSnapshotMapper ruleRuningSnapshotMapper;

	@Resource
	IRuleRuningSnapshotDao ruleRuningSnapshotDao;
	
	private static String replayBatchNo = null;

	static ObjectMapper objectMapper;

	private final Logger logger = LoggerFactory.getLogger(RuleRunigSnapshotServiceImpl.class);

	@Autowired
    private RedisTemplate redisTemplate;
	
	@Autowired
    private WebMqttComponent webMqttComponent;
	
	private static String PACKAGEURL = "com.yeejoin.amos.fas.business.action.result.message.";
	
	static {
		objectMapper = new ObjectMapper();
	}

//	public RuleRunigSnapshotServiceImpl(IRuleRuningSnapshotRepository repository)
//	{
//		super(repository);
//		// TODO Auto-generated constructor stub
//		this.repository = repository;
//		ExecuteMethodHook.hook.add(this);
//	}
//
//
	/**
	 * 保存动作记录
	 * @param methodName
	 * @param paramsAndTypes
	 * @param matchedObj
	 */
	public void process(Object  bean,String methodName,String paramsAndTypes,String batchNo)
	{

		if(bean instanceof CustomerAction)
		{
//			Set set = (Set) matchedObj;
//			BasicsRo fireEquimentDataRo = (BasicsRo) set.iterator().next();

			RuleRuningSnapshot ruleRuningSnapshot = new RuleRuningSnapshot();
			ruleRuningSnapshot.setId(UUID.randomUUID().toString());
			ruleRuningSnapshot.setMethodClass(bean.getClass().getName());
			ruleRuningSnapshot.setMethodName(methodName);
			ruleRuningSnapshot.setMethodParam(paramsAndTypes);
			ruleRuningSnapshot.setBatchNo(batchNo);
			//ruleRuningSnapshot.setPackageId(fireEquimentDataRo.getPackageId());
			//ruleRuningSnapshot.setEquipmentId(String.valueOf(fireEquimentDataRo.getId()));

			Date now = new Date();
			ruleRuningSnapshot.setCreateTime(now);
			ruleRuningSnapshot.setCreateMillisecond(String.valueOf(now.getTime()));
			ruleRuningSnapshot.setPreviousInterval(0L);
			RuleRuningSnapshot oldEntity  = ruleRuningSnapshotMapper.querForObject(batchNo);
			if(oldEntity != null)
				ruleRuningSnapshot.setPreviousInterval(now.getTime() - Long.parseLong(oldEntity.getCreateMillisecond()));
			//repository.save(ruleRuningSnapshot);
			ruleRuningSnapshotDao.save(ruleRuningSnapshot);
		}
	}
	
	@Transactional
	public void reacordPlan(String topic, String msgType, String msgBody, Object contingency) 
	{
		String batchNo = null;
		if (contingency instanceof ContingencyRo) {
			batchNo = ((ContingencyRo) contingency).getBatchNo();
		} else if (contingency instanceof String) {
			batchNo = contingency.toString();
		} else {
			batchNo = (String) redisTemplate.opsForValue().get(RiskSourceServiceImpl.cacheKeyForCanBeRunning());
		}
		RuleRuningSnapshot ruleRuningSnapshot = new RuleRuningSnapshot();
		ruleRuningSnapshot.setId(UUID.randomUUID().toString());
		ruleRuningSnapshot.setMethodClass("com.yeejoin.amos.fas.business.action.ContingencyAction");
		ruleRuningSnapshot.setPackageId(topic);
		ruleRuningSnapshot.setMethodName(msgType);
		ruleRuningSnapshot.setMethodParam(msgBody);
		ruleRuningSnapshot.setBatchNo(batchNo);

		Date now = new Date();
		ruleRuningSnapshot.setCreateTime(now);
		ruleRuningSnapshot.setCreateMillisecond(String.valueOf(now.getTime()));
		ruleRuningSnapshot.setPreviousInterval(0L);
		RuleRuningSnapshot oldEntity  = ruleRuningSnapshotMapper.querForObject(batchNo);
		if(oldEntity != null)
			ruleRuningSnapshot.setPreviousInterval(now.getTime() - Long.parseLong(oldEntity.getCreateMillisecond()));
		ruleRuningSnapshotDao.save(ruleRuningSnapshot);	
	}

	@Async
	public void replayPlan(String batchNo, String randomNumber)  throws Exception
	{
		try
		{
			List<RuleRuningSnapshot> oldEntityList=ruleRuningSnapshotMapper.querForObjectList(batchNo);

			if(!CollectionUtils.isEmpty(oldEntityList))
			{
				logger.info("开始回放：batchNo="+batchNo);
				logger.info("获取到动作记录个数："+oldEntityList.size());
				redisTemplate.opsForValue().set("replay:" + batchNo +":" + randomNumber, true);
				int count = 0;
				for(RuleRuningSnapshot snapshot : oldEntityList)
				{
					if(batchNo == null)
						return ;
					Object play =  redisTemplate.opsForValue().get("replay:" + batchNo +":" + randomNumber);
					if (ObjectUtils.isEmpty(play)) {
						SafteyPlanResult result = new SafteyPlanResult();
				        Map<String, Object> tempmap1 = new HashMap<>();

				        tempmap1.put("type", "event");
				        tempmap1.put("content", "stopPlan");
				        result.add(tempmap1);
				        
				        Constructor<?> constructor = Class.forName(
			                    PACKAGEURL + result.getClass().getSimpleName() + "Message")
			                    .getConstructor(ActionResult.class);
				        AbstractActionResultMessage<?> action = (AbstractActionResultMessage<?>) constructor.newInstance(result);
				        ToipResponse toipResponse = action.buildResponse("message", batchNo, result.toJson());
						
				        webMqttComponent.publish(String.format("%s/%s/%s", snapshot.getPackageId(), "replay", randomNumber), toipResponse.toJsonStr());
				        return;
					}
					//延迟
					logger.info("开始执行第"+(++count)+"个动作.");
					logger.info("方法名："+snapshot.getMethodClass()+"."+snapshot.getMethodName());
					logger.info("需要延迟"+snapshot.getPreviousInterval()+"毫秒.......");
					Thread.sleep(snapshot.getPreviousInterval());

					try
					{
						webMqttComponent.publish(String.format("%s/%s/%s", snapshot.getPackageId(), "replay", randomNumber) , snapshot.getMethodParam());
						logger.info("第"+(count)+"个动作执行成功.");
					}catch (Exception e)
					{
						logger.info("第"+(count)+"个动作执行失败.");
						e.printStackTrace();
						logger.error(e.getMessage(),e);
					}

				}
			}
		}catch (Exception e)
		{
			logger.info("回放失败.");
			logger.error(e.getMessage(),e);
		} finally {
			redisTemplate.delete("replay:" + batchNo +":" + randomNumber);
		}
	}
	
	@Override
	public void exitReplayPlan(String batchNo, String randomNumber) {
		redisTemplate.delete("replay:" + batchNo +":" + randomNumber);
	}
	
	@Async
	public void replay(String batchNo) throws Exception
	{

		if(replayBatchNo != null)
			throw new Exception("一次只能回放一个预案记录.");

		try
		{
			replayBatchNo = batchNo;
			List<RuleRuningSnapshot> oldEntityList=ruleRuningSnapshotMapper.querForObjectList(batchNo);
		//	List<RuleRuningSnapshot> oldEntityList  = repository.querForObjectList(batchNo);

			if(!CollectionUtils.isEmpty(oldEntityList))
			{
				logger.info("开始回放：batchNo="+batchNo);
				logger.info("获取到动作记录个数："+oldEntityList.size());

				int count = 0;
				for(RuleRuningSnapshot snapshot : oldEntityList)
				{
					if(replayBatchNo == null)
						return ;

					//延迟
					logger.info("开始执行第"+(++count)+"个动作.");
					logger.info("方法名："+snapshot.getMethodClass()+"."+snapshot.getMethodName());
					logger.info("需要延迟"+snapshot.getPreviousInterval()+"毫秒.......");
					Thread.sleep(snapshot.getPreviousInterval());

					try
					{
						Class clzz = Class.forName(snapshot.getMethodClass());
						Object obj = SpringContextHelper.getBean(clzz);
						Method[] methods=clzz.getMethods();
						if(!StringUtils.isEmpty(snapshot.getMethodParam()))
						{
							for(Method method:methods)
							{
								if(replayBatchNo == null)
									return ;

								String name=method.getName();
								if(!name.equals(snapshot.getMethodName())){
									continue;
								}
								Map paramsMap 			= objectMapper.readValue(snapshot.getMethodParam(), Map.class);
								List<String> datatypes 	= (List<String>)paramsMap.get("datatypes");
								List<Object> values    	= (List<Object>)paramsMap.get("values");

								Object[] params = new Object[values.size()];
								for(int i = 0;i<datatypes.size();i++)
								{
									if(replayBatchNo == null)
										return ;

									String typeStr = datatypes.get(i);
									Object value = values.get(i);
									String valueStr = objectMapper.writeValueAsString(value);
									Class valueClzz =  Class.forName(typeStr);
									params[i] = objectMapper.readValue(valueStr,valueClzz);
								}
								method.invoke(obj,params);
							}
						}else{
							Method method = clzz.getMethod(snapshot.getMethodName(),new Class[]{});
							method.invoke(obj);
						}

						logger.info("第"+(count)+"个动作执行成功.");
					}catch (Exception e)
					{
						logger.info("第"+(count)+"个动作执行失败.");
						e.printStackTrace();
						logger.error(e.getMessage(),e);
					}

				}
			}
		}catch (Exception e)
		{
			logger.info("回放失败.");
			logger.error(e.getMessage(),e);
		}finally {
			replayBatchNo = null;
		}

	}


	public static String getReplayBatchNo() {
		return replayBatchNo;
	}

	public static void setReplayBatchNoToNull() {
		RuleRunigSnapshotServiceImpl.replayBatchNo = null;
	}

	
}