package com.persagy.functions.weather.thread;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import com.persagy.ems.pojo.wz.DayHisRecord;
import com.persagy.ems.pojo.wz.DayPredictRecord;
import com.persagy.framework.util.BeanUtils;
import com.persagy.framework.util.ConfigUtil;
import com.persagy.framework.util.DateUtils;
import com.persagy.framework.util.WServiceUtil;
import com.persagy.functions.weather.constant.Const;
import com.persagy.functions.weather.dto.CityDTO;
import com.persagy.functions.weather.dto.DayPredictDTO;
import com.persagy.functions.weather.service.DayHisRecordService;

/**
 * 日天气数据记录
 */
public class DayDataRecordThread implements Runnable {
	private static final Logger log = Logger.getLogger(DayDataRecordThread.class);

	private static Set<String> allRecordCity = new HashSet<>();

	private DayHisRecordService dayHisRecordService;
	
	private static final String tname = "dayRecord";

	public DayDataRecordThread(DayHisRecordService hisService) {
		this.dayHisRecordService = hisService;
	}


	private void doLogic(Date dayTime, int hourIndex, Set<String> hasTodayRecordCity, Set<String> has14PredictCity,
						 long startT) throws Exception {
		int saveNum = 0;
		int updateNum = 0;
		int predicNum = 0;
		for(String cityId : allRecordCity) {
			if(hourIndex < 23) {
				if(hasTodayRecordCity.contains(cityId) && has14PredictCity.contains(cityId)) {
					// 对于当天23点之前已进行过日预报数据访问的，跳过有今日数据并且有预报数据的城市
					// 23点之后对所有城市更新一遍数据
					continue;
				}
			}

			List<DayPredictDTO> predicDataList = WServiceUtil.getDayPredictObj(cityId, 0, 15);
			if(!predicDataList.isEmpty()) {
				dayHisRecordService.coverPredictRecordUseDTO(cityId, predicDataList);
				predicNum ++;

				DayPredictDTO predicData = predicDataList.get(0);
				predicData.setDate(null);
				DayHisRecord saveRecord = new DayHisRecord();
				if (predicData.getLast_update() != null) {
					saveRecord.setLastUpdate(
							DateUtils.t8DateStr2Date(predicData.getLast_update()));
					predicData.setLast_update(null);
				}
				BeanUtils.copyBean(predicData, saveRecord);
				saveRecord.setDayTime(dayTime);
				saveRecord.setCityId(cityId);
				saveRecord.setUpdateTime(new Date());

				if (hasTodayRecordCity.contains(cityId)) {
					try {
						this.dayHisRecordService.updateOne(cityId, dayTime, saveRecord);
						updateNum ++;
					}catch (Exception e) {
						log.error(tname + " update error, city="+cityId+", errMsg=" + e.getMessage());
					}
				} else {
					try {
						this.dayHisRecordService.save(saveRecord);
						hasTodayRecordCity.add(cityId);
						saveNum ++;
					}catch (Exception e) {
						log.error(tname + " save error, city="+cityId+", errMsg=" + e.getMessage());
					}
				}

				try {
					Thread.sleep(100);
				}catch (Exception e) {
				}
			}
		}

		long endT = System.currentTimeMillis();
		String seconCost = String.format("%.2f", (endT - startT) / 1000.0);
		log.info(tname + " hour["+hourIndex+"] executed result: totalCity=" + allRecordCity.size() + ", today:save-update=" +
				saveNum + "-" + updateNum + ", predict:update="+predicNum+", timeCost:" + seconCost + "s");
	}

	private void updateMap() {
		Map<String, CityDTO> id2cityMap = ConfigUtil.getId2City();
		Set<String> hourCitySet = ConfigUtil.getHourCitySet();
		Set<String> dayHisCitySet = new HashSet<>();
		if(null != id2cityMap) {
			String pinyin = null;
			for(String cityId : id2cityMap.keySet()) {
				pinyin = id2cityMap.get(cityId).getPinyin();
				if(hourCitySet.contains(cityId) || (null != pinyin && pinyin.trim().length()>2)) {
					dayHisCitySet.add(cityId);
				}
			}
		}

		if(!dayHisCitySet.isEmpty()) {
			log.info("day city total " + dayHisCitySet.size());
			int addNum = 0;
			for(String cityId : dayHisCitySet) {
				if(!allRecordCity.contains(cityId)) {
					addNum ++;
				}
				allRecordCity.add(cityId);
			}
			if(addNum > 0) {
				log.info("day city add " + addNum);
			}
		}
	}


	@Override
	public void run() {

		log.info(tname+" 线程启动...");
		while(true) {
			updateMap();

			if(!allRecordCity.isEmpty()) {

				Date dateNow = new Date();
				Date dayTime = null, dayHour = null;
				try {
					dayTime = DateUtils.getShortDate(dateNow, 3);
					dayHour = DateUtils.getShortDate(dateNow, 2);
				} catch (Exception e1) {
					e1.printStackTrace();
				}
				
				Date day14Start = DateUtils.getDayOff(dayTime, 14);
				int hourIndex = DateUtils.getHour(dateNow);

				Long sleepMinlliseconds = Const.AboutTime.minMinlliseconds;

				if(hourIndex < 2) {
					// 凌晨2小时前不更新数据
					sleepMinlliseconds = DateUtils.getHourOff(dayHour, 1).getTime() - new Date().getTime();
					log.info(tname + ", before 02 hour, do nothing");
				}else {

					log.info(tname + ", query cities that has today record...");
					Set<String> hasTodayRecordCity = new HashSet<>();
					List<DayHisRecord> haveRecordCities = null;
					try {
						haveRecordCities = this.dayHisRecordService.queryOneDay(dayTime);
					} catch (Exception e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					if (haveRecordCities != null) {
						for (DayHisRecord item : haveRecordCities) {
							hasTodayRecordCity.add(item.getCityId());
						}
					}
					log.info(tname + ", has today record cityNum : " + hasTodayRecordCity.size());


					log.info(tname + ", query cities that has " + day14Start + " dayPredict record...");
					Set<String> has14PredictCity = new HashSet<>();
					List<DayPredictRecord> havePredicDataCities = null;
					try {
						havePredicDataCities = this.dayHisRecordService.queryPredictDataOfOneDay(day14Start);
					} catch (Exception e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					// 有不完整数据的城市数量
					int wrong14PredictNum = 0;
					if (havePredicDataCities != null) {
						Pattern pattern = Pattern.compile("^-?\\d+(\\.\\d+)?$");
						for (DayPredictRecord item : havePredicDataCities) {
							// 有有效的预报数据
							try {
								if(pattern.matcher(item.getHigh()).matches() &&
										pattern.matcher(item.getLow()).matches() &&
										pattern.matcher(item.getHumidity()).matches()) {

									has14PredictCity.add(item.getCityId());
								}else {
									wrong14PredictNum ++;
								}
							}catch (Exception e) {
								wrong14PredictNum ++;
							}
						}
					}else {
						wrong14PredictNum = allRecordCity.size();
					}
					log.info(tname + ", has dayPredict record cityNum : " + has14PredictCity.size() + ". wrong14PredictNum=" + wrong14PredictNum);

					long startT = System.currentTimeMillis();

					try {
						doLogic(dayTime, hourIndex, hasTodayRecordCity, has14PredictCity, startT);
					} catch (Exception e1) {
						e1.printStackTrace();
					}

					if(hourIndex < 23) {
						// 下一执行时间设置为半小时后
						Date nextTime = DateUtils.getMinuteOff(dateNow, 30);

						// 下一执行时间的小时索引
						int nextHour = DateUtils.getHour(nextTime);

						if(nextHour == 23) {
							try {
								sleepMinlliseconds = DateUtils.getShortDate(nextTime, 2).getTime() - dateNow.getTime() + 1000L;
							} catch (Exception e) {
								e.printStackTrace();
							}
						}else {
							// 23点前，每隔半小时再执行一次逻辑，对访问出错的城市进行数据补查
							sleepMinlliseconds = Const.AboutTime.min30Minlliseconds;
						}
					}else {
						sleepMinlliseconds = Const.AboutTime.hourMilliseconds;
					}
				}

				log.info(tname + " thread sleep " + String.format("%.2f", sleepMinlliseconds.longValue() / 60000.0)  + "min");
				try {
					Thread.sleep(sleepMinlliseconds);
				}catch (Exception e) {
					e.printStackTrace();
				}
			}else {
				log.info(tname + " executed. no city need to record day data, sleep 1 min");
				try {
					Thread.sleep(Const.AboutTime.minMinlliseconds);
				}catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}
