package io.renren.modules.job.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import io.renren.common.constant.Constant;
import io.renren.common.utils.DateUtils;
import io.renren.common.utils.HttpClientUtil;
import io.renren.modules.job.service.PushService;
import io.renren.modules.sys.dao.CallBackDao;
import io.renren.modules.sys.dao.CompaniesDao;
import io.renren.modules.sys.dao.CustomersDao;
import io.renren.modules.sys.dao.LandingPagesDao;
import io.renren.modules.sys.dao.ScopeCityCompanyDao;
import io.renren.modules.sys.dao.ScopeCityDao;
import io.renren.modules.sys.dto.CompanyWeightDTO;
import io.renren.modules.sys.entity.CallBackEntity;
import io.renren.modules.sys.entity.CompaniesEntity;
import io.renren.modules.sys.entity.CustomersEntity;
import io.renren.modules.sys.entity.LandingPagesEntity;
import io.renren.modules.sys.entity.ScopeCityCompanyEntity;
import io.renren.modules.sys.entity.ScopeCityEntity;
import lombok.extern.log4j.Log4j2;

@Service("pushServiceImpl")
@Log4j2
public class PushServiceImpl implements PushService {
	
	@Autowired
	private CustomersDao customersDao;
	
	@Autowired
	private ScopeCityDao scopeCityDao;
	
	@Autowired
	private LandingPagesDao landingPagesDao;
	
	@Autowired
	private ScopeCityCompanyDao scopeCityCompanyDao;
	
	@Autowired
	private CompaniesDao companiesDao;
	
	@Autowired
	private CallBackDao callBackDao;
	
	@Autowired
	private RedisTemplate<String, Object> redisTemplate;

	public static String visitsUrl ="https://analytics.oceanengine.com/api/v2/conversion";
	
	@Override
	public void customerTask() {
		log.info("客户信息推送定时任务开始");
		
		/**
		 * 第1步
		 * 获取要推送的数据
		 * 查询条件为 status IS NULL AND (is_push = 0 OR is_push IS NULL)
		 */
		QueryWrapper<CustomersEntity> customerQw = new QueryWrapper<CustomersEntity>()       // .isNull("status")
				.and(i -> i.eq("status", Constant.Status.ENABLE.getValue()).or().isNull("status") )
				.and(i -> i.eq("is_push", Constant.PushStatus.UN_PUSH.getValue()).or().isNull("is_push") )/* .eq("company_id", 0L) */;
		List<CustomersEntity> customerList = customersDao.selectList(customerQw);
		for (CustomersEntity customer : customerList) {
			
			/**
			 *如果落地页指定公司，直接推送（直投）,页面接收的时候，就分配了公司
			 *推送失败（再次推送）
			 */
			if (customer.getCompanyId().intValue() >0) {
				//TODO重推
				
				Long companyId = customer.getCompanyId();
				CompaniesEntity companiesEntity = companiesDao.selectOne(new QueryWrapper<CompaniesEntity>()
						.eq("id", companyId).eq("status", Constant.Status.ENABLE.getValue()).last("limit 1"));
				
				push(companiesEntity.getUrl(), customer);
				continue;
				
			}
			
			 QueryWrapper<LandingPagesEntity> wrapper = new QueryWrapper<>();
	        wrapper.eq(StringUtils.isNotBlank(customer.getPageId().toString()), "id", customer.getPageId())
	        		.eq("status",Constant.Status.ENABLE.getValue());
	        LandingPagesEntity landingPage= landingPagesDao.selectOne(wrapper);
	       // landingPage.getCompanyIds();
	        
	        
	        
	        customer.setChannelId(landingPage.getChannelId());
	        customer.setOperationId(landingPage.getOperationId());
	        
//	        if(StringUtils.isNotEmpty(landingPage.getCompanyIds())) {
//	        	pushMsg(customer, landingPage);
//				continue;
//	        }
	        
	        
			/**
			 * 第2步
			 * 根据PageId和city获取落地城市
			 * 未获取到则更新状态为无效，流程结束
			 */
			QueryWrapper<ScopeCityEntity> scopeCityQw = new QueryWrapper<>();
			scopeCityQw.eq("page_id", customer.getPageId());
			scopeCityQw.eq("city", customer.getCity());
			scopeCityQw.eq("status", Constant.Status.ENABLE.getValue());
			scopeCityQw.last("limit 1");
			ScopeCityEntity scopeCity = scopeCityDao.selectOne(scopeCityQw);
			if(null == scopeCity) {
				errorUpdate(customer, "未找到落地城市");
				continue;
			}
			
			/**
			 * 第3步
			 * 根据落地城市获取公司列表
			 * 未获取到则更新状态为无效，流程结束
			 */
			List<CompanyWeightDTO> companyList = getCompanyList(scopeCity.getId(),customer.getMobile());
			if(companyList.isEmpty()) {
				errorUpdate(customer, "落地城市"+scopeCity.getId()+"下没有符合条件的公司");
				continue;
			}
			
			/**
			 * 第4步
			 * 根据投放策略选取公司
			 */
			int pos = getPos(companyList.size(), scopeCity.getStrategy());
			CompanyWeightDTO companyDto = companyList.get(pos);
			customer.setCompanyId(companyDto.getCompanyId());

			//ads
			customer.setAdsId(HttpClientUtil.getUrlParam(customer.getUrl(),"adid"));
			customer.setCreativeId(HttpClientUtil.getUrlParam(customer.getUrl(),"creativeid"));

			//公司已分配人数+1
			String countKey = Constant.COMPANY_COUNT_KEY+companyDto.getCompanyId();
			Long count = (Long)redisTemplate.opsForValue().get(countKey);
			redisTemplate.opsForValue().set(countKey,null == count ? 1 : ++count);
			
			/**
			 * 第5步
			 * 回调公司表里的url推送数据，更新customer表，新增反馈表
			 */
			push(companyDto.getUrl(), customer);
		}
	}
	
	/**
	 * 失败且不再推送
	 * @param customer
	 * @param msg
	 */
	private void errorUpdate(CustomersEntity customer,String msg) {
		String logStr = String.format("客户%s,姓名%s,手机号%s ;",customer.getId(),customer.getName(),customer.getMobile());
		log.error(logStr+msg);
		customer.setStatus(Constant.Status.DISABLE.getValue());
		customer.setReturnMsg(msg);
		customer.setIsPush(Constant.PushStatus.UN_PUSH.getValue());
		customersDao.updateById(customer);
	}
	
	
	/**
	 * 直接投放
	 * @param customer
	 * @param msg
	 */
	private void pushMsg(CustomersEntity customer,LandingPagesEntity landingPage) {
//		String logStr = String.format("客户%s,姓名%s,手机号%s ;",customer.getId(),customer.getName(),customer.getMobile());
//		log.error(logStr+msg);
//		customer.setStatus(Constant.Status.DISABLE.getValue());
//		customer.setReturnMsg(msg);
//		customer.setIsPush(Constant.PushStatus.UN_PUSH.getValue());
		customersDao.updateById(customer);
	}
	
	/**
	 * 根据落地城市获取公司列表
	 * @param scopeCityId
	 * @param mobile
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private List<CompanyWeightDTO> getCompanyList(Long scopeCityId,String mobile) {
		//从缓存中获取
		List<CompanyWeightDTO> companyWeightList = new ArrayList<>();
		String key = Constant.SCOPE_CITY_KEY+scopeCityId;
		//从缓存中根据落地城市获取对应公司，没有则查数据库
		//不能从缓存中取，否则没发判断去重，以及分配人数大于应该分配的人
//		Object obj = redisTemplate.opsForValue().get(key);
//		if(null != obj) {
//			companyWeightList = (List<CompanyWeightDTO>) obj;
//			return companyWeightList;
//		}
		
		//缓存中没有，从数据库中获取
		QueryWrapper<ScopeCityCompanyEntity> cityCompanyQw = new QueryWrapper<>();
		cityCompanyQw.eq("scope_city_id", scopeCityId);
		cityCompanyQw.eq("status", Constant.Status.ENABLE.getValue());
		List<ScopeCityCompanyEntity> cityCompanyList = scopeCityCompanyDao.selectList(cityCompanyQw);
		for (ScopeCityCompanyEntity scopeCityCompanyEntity : cityCompanyList) {
			Long companyId = scopeCityCompanyEntity.getCompanyId();
			CompaniesEntity companiesEntity = companiesDao.selectOne(new QueryWrapper<CompaniesEntity>()
					.eq("id", companyId).eq("status", Constant.Status.ENABLE.getValue()).last("limit 1"));
			if(null == companiesEntity) {
				log.error("公司{}不存在或已停用",companyId);
				continue;
			}
			
			//验证已分配人数是否大于等于可分配人数，大于则跳过
			if(personCount(companyId, scopeCityCompanyEntity.getAssignPersonCount(), key)) {
				log.error("公司{}当天已分配人数大于可分配人数，跳过",companyId);
				continue;
			}
			
			//去重校验
			if(null != companiesEntity.getRepeatFlag() && 1 == companiesEntity.getRepeatFlag()) {
				//查询当前手机号是否已经在表里
				Long repeatCount = customersDao.selectCount(new QueryWrapper<CustomersEntity>()
						.eq("mobile", mobile).eq("company_id", companyId).eq("status", Constant.Status.ENABLE.getValue()));
				if(repeatCount > 0) {
					log.error("客户{}已在公司{}中，去重跳过",mobile,companyId);
					continue;
				}
			}
			
			//计算权重
			Integer weight = scopeCityCompanyEntity.getWeight() == null ? 0 : scopeCityCompanyEntity.getWeight();
			for (int i = 0; i < weight; i++) {
				CompanyWeightDTO dto = new CompanyWeightDTO();
				BeanUtils.copyProperties(scopeCityCompanyEntity, dto);
				BeanUtils.copyProperties(companiesEntity, dto);
				companyWeightList.add(dto);
			}
		}
		if(!companyWeightList.isEmpty()) {
			redisTemplate.opsForValue().set(key,companyWeightList);
		}
		return companyWeightList;
	}
	
	/**
	 * 判断公司已分配人数是否大于等于可分配人数，是返回true
	 * @param companyId
	 * @param assignCount
	 * @return
	 */
	private Boolean personCount(Long companyId,Integer assignCount,String scopeCityKey) {
		String countKey = Constant.COMPANY_COUNT_KEY+companyId;
		//公司已分配人数
		Long userCount = (Long)redisTemplate.opsForValue().get(countKey);
		if(null == userCount) {
			//公司当天已分配人数
			userCount = customersDao.selectCount(new QueryWrapper<CustomersEntity>()
					.eq("status", Constant.Status.ENABLE.getValue())
					.eq("company_id", companyId)
					.between("create_date", DateUtils.getNowStart(), DateUtils.getNowEnd()));
		}
		if(userCount >= assignCount) {
			log.error("可分配人数"+assignCount+",已分配人数："+userCount+",不分配给该公司");
			//删除公司列表缓存和已分配人数缓存,重新计算
			redisTemplate.delete(Arrays.asList(scopeCityKey,countKey));
			return true;
		}
		return false;
	}
	
	/**
	 * 
	 * @param listSize
	 * @param strategy 投放策略1轮询2随机
	 * @return
	 */
	private int getPos(int listSize,Integer strategy) {
		Integer pos = 0;
		try {
			//轮询
			if(null != strategy && 1 == strategy) {
				Object object = redisTemplate.opsForValue().get(Constant.POS_KEY);
				if (object == null) {
					// 获取中不存在当前轮询位置时从0开始
					pos = 0;
				} else {
					pos = Integer.valueOf(object.toString());
					// 当轮询位置+1
					pos++;
					if (pos >= listSize) {
						// 重置
						pos = 0;
					}
				}
				redisTemplate.opsForValue().set(Constant.POS_KEY, pos);
			}else {
				//随机
				pos = new Random().nextInt(listSize);
			}
			
		} catch (Throwable e) {
			log.error("获取缓存发生异常:",e);
		}
		return pos;
	}
	
	/**
	 * 推送
	 * @param url
	 * @param customer
	 */
	private void push(String url,CustomersEntity customer) {
		//判断公司是否有url，有就调接口
		if(StringUtils.isBlank(url)) {
			log.error("公司"+customer.getCompanyId()+"没有没有配置url,不处理");
			return;
		}
		
		String resCode = "";
		String resMsg ="";

		String[] excludeProperties = {"telLocation", "operationId","channelId","companyId","pageId","ip","leadsArea","isPush","pushMsg","pushDate",
				"pushStatus","returnMsg","accountId","accountName","adgroupId",
				"adgroupName","adsName","creativeName","updateBy","updateTime","deleteTime","createDate","status","url"};
		PropertyPreFilters filters = new PropertyPreFilters();
		PropertyPreFilters.MySimplePropertyPreFilter excludeFilter = filters.addFilter();
		excludeFilter.addExcludes(excludeProperties);

		String pushMsg = JSONObject.toJSONString(customer, excludeFilter, SerializerFeature.PrettyFormat,SerializerFeature.NotWriteDefaultValue);
		try {
			if (url.equals(Constant.LOCAL_SYSTEM)) {
				resMsg ="{\"code\":200,\"msg\":\"本地保存，添加成功!\"}";
			}else{
				resMsg = HttpClientUtil.post(url, JSONObject.parseObject(pushMsg));
			}
			resCode = JSONObject.parseObject(resMsg).getString("code");
			customer.setStatus(Constant.Status.ENABLE.getValue());
		} catch (Exception e) {
			log.error("调用公司URL异常",e.getMessage());
			customer.setStatus(Constant.Status.DISABLE.getValue());
		}

		customer.setPushStatus(resCode);
		customer.setPushMsg(pushMsg);
		customer.setReturnMsg(resMsg);
		customer.setPushDate(new Date());

		if (StringUtils.isNotEmpty(resCode) && resCode.equals("200")){
			customer.setIsPush(Constant.PushStatus.ALREADY_PUSH.getValue());
			customersDao.updateById(customer);
		}else{
			customer.setIsPush(Constant.PushStatus.UN_PUSH.getValue());
			if (DateUtils.getDifferMin(customer.getCreateDate())>10){
				customer.setStatus(Constant.Status.DISABLE.getValue());
				customer.setReturnMsg("超过10分钟没有推送成功");
			}
			customersDao.updateById(customer);
			return;
		}

		if (StringUtils.isNotEmpty(customer.getUrl())){
			String clickId = HttpClientUtil.getUrlParam(customer.getUrl(),"clickid");
			if (StringUtils.isNotEmpty(clickId)) {
				//组装反馈表参数
				CallBackEntity callBack = new CallBackEntity();
				String formCode = "500";
				String zijieresp = null;
				String zijieStr = "{\"event_type\":\"form\",\"context\":{\"ad\":{\"callback\":\""+clickId+"\"}},\"timestamp\":"+System.currentTimeMillis()+"}";
				try {
					zijieresp = HttpClientUtil.post(visitsUrl, JSONObject.parseObject(zijieStr));
					formCode = JSONObject.parseObject(zijieresp).getString("code");
					callBack.setStatus(Constant.Status.ENABLE.getValue());//状态（1有效、0无效）
				} catch (Exception e) {
					log.error("调用字节异常",e.getMessage());
					callBack.setStatus(Constant.Status.DISABLE.getValue());//状态（1有效、0无效）
				}
				
				callBack.setCustomerId(customer.getId());//用户id
				callBack.setCompanyId(customer.getCompanyId());//公司ID
				callBack.setContent(JSONObject.toJSONString(zijieStr));//推送内容
				callBack.setPushCode(formCode);//推送返回状态码
				callBack.setPushMsg(zijieresp);//推送返回信息
				callBack.setPushDate(new Date());//推送时间

				if (StringUtils.isNotEmpty(formCode) && formCode.equals("0")){
					callBack.setCallType("1");//有效表单
					callBack.setIsPush(Constant.PushStatus.ALREADY_PUSH.getValue());//是否已推送1是0否
				}else{
					callBack.setCallType("0");//未回传
					callBack.setStatus(Constant.Status.DISABLE.getValue());
					callBack.setIsPush(Constant.PushStatus.UN_PUSH.getValue());//是否已推送1是0否
				}
				callBackDao.insert(callBack);
			}
		}
	}

	@Override
	public void callBackTask() {
		log.info("反馈定时任务正在执行");
		
		QueryWrapper<CallBackEntity> qw = new QueryWrapper<>();
    	qw.eq("is_push", Constant.PushStatus.UN_PUSH.getValue());
    	qw.eq("status", Constant.Status.ENABLE.getValue());
		List<CallBackEntity> list = callBackDao.selectList(qw);
		for (CallBackEntity entity : list) {
			try {
				CustomersEntity customer = customersDao.selectById(entity.getCustomerId());
				if(null == customer) {
					log.error("根据客户id{}未找到对应的客户信息",entity.getCustomerId());
					errorCallBack(entity,"根据客户id"+entity.getCustomerId()+"未找到对应的客户信息");
					continue;
				}
				if (entity.getCallType().equals("0")  || entity.getCallType().equals("1")  ){
					errorCallBack(entity,"未回访,表单提交 不进行反馈");
					continue;
				}
				String reply_code=Constant.VisitMap.get(entity.getCallType());
				if (StringUtils.isEmpty(reply_code)){
					errorCallBack(entity,"未找到对应反馈CallType");
					continue;
				}

				if (StringUtils.isNotEmpty(customer.getUrl())) {
					String clickId = HttpClientUtil.getUrlParam(customer.getUrl(), "clickid");
					if (StringUtils.isNotEmpty(clickId)) {
						String str = "{\"event_type\":\""+reply_code+"\",\"context\":{\"ad\":{\"callback\":\""+clickId+"\"}},\"timestamp\":"+System.currentTimeMillis()+"}";

						String resp = HttpClientUtil.post(visitsUrl, JSONObject.parseObject(str));
						//String test = "{\"code\":0,\"message\":\"成功\"}";
						Map<String,Object> respMsg = JSONObject.parseObject(resp, Map.class);

						//推送后更新数据
						entity.setIsPush(Constant.PushStatus.ALREADY_PUSH.getValue());
						entity.setContent(str);
						entity.setPushDate(new Date());
						entity.setPushMsg(resp);
						entity.setPushCode(String.valueOf(respMsg.get("code")));
						callBackDao.updateById(entity);
					}
				}
			} catch (Exception e) {
				log.error("推送回访信息定时任务处理失败,Visits id={}",entity.getId(),e);
			}
		}
		
	}

	/**
	 * 失败且不再推送
	 * @param callBack
	 * @param msg
	 */
	private void errorCallBack(CallBackEntity callBack,String msg) {
		String logStr = String.format("callBackID:%s,错误:%s ;",callBack.getId(),msg);
		log.error(logStr+msg);
		//推送后更新数据
		callBack.setStatus(Constant.Status.DISABLE.getValue());
		callBack.setUpdateTime(new Date());
		callBack.setContent(msg);
		callBackDao.updateById(callBack);
	}
	
	/**
	 * 表单处理 用JSONObject.parseObject(JSONObject.toJSONString(customer))替换了
	 * @param entity
	 * @return
	 */
	@Deprecated
	public JSONObject processEntity(CustomersEntity entity){
		JSONObject obj = new JSONObject();
		obj.put("id", System.currentTimeMillis());
		obj.put("customerId", entity.getId());
		obj.put("name", entity.getName());
		obj.put("sex",entity.getSex());
		obj.put("mobile", entity.getMobile());
		obj.put("telLocation",entity.getTelLocation());
		obj.put("age", entity.getAge());
		obj.put("city", entity.getCity());//城市
		obj.put("cardNo", entity.getCardNo());
		obj.put("url", entity.getUrl());

		obj.put("address", entity.getAddress());
		obj.put("bundle",entity.getBundle());
		obj.put("isMarried", entity.getIsMarried());//婚姻状况(0未婚1已婚)

		obj.put("education", entity.getEducation());
		obj.put("professional", entity.getProfessional());
		obj.put("employmentTime", entity.getEmploymentTime());
		obj.put("monthlyIncome", entity.getMonthlyIncome());
		obj.put("payrollType", entity.getPayrollType());
		obj.put("money", entity.getMoney());
		obj.put("useFor", entity.getUseFor());
		obj.put("loanLongTime", entity.getLoanLongTime());
		obj.put("creditSituation", entity.getCreditSituation());
		obj.put("IsCredit", entity.getIsCredit());
		obj.put("sheBao", entity.getShebao());
		obj.put("gongJiJin", entity.getGongjijin());
		obj.put("car", entity.getCar());
		obj.put("carType", entity.getCarType());
		obj.put("carModel", entity.getCarModel());
		obj.put("carLife", entity.getCarLife());
		obj.put("house", entity.getHouse());
		obj.put("houseType", entity.getHouseType());
		obj.put("houseSize", entity.getHouseSize());
		obj.put("houseLife", entity.getHouseLife());
		obj.put("baoDanIs", entity.getBaodanIs());
		obj.put("zmScore", entity.getZmScore());
		obj.put("weiLi", entity.getWeili());
		obj.put("businessLicense", entity.getBusinessLicense());
		obj.put("shopName", entity.getShopName());
		obj.put("shopAddress", entity.getShopAddress());
		obj.put("leadsEmail", entity.getLeadsEmail());
		obj.put("leadsqQQ", entity.getLeadsQq());
		obj.put("leadsWechat",entity.getLeadsWechat() );
		obj.put("remark", entity.getRemark());
		return  obj;
	}
}
