package com.ym.mec.biz.service.impl;

import com.ym.mec.biz.dal.dao.OrganizationDao;
import com.ym.mec.biz.dal.dto.IndexBaseDto;
import com.ym.mec.biz.dal.entity.Organization;
import com.ym.mec.biz.dal.enums.GroupType;
import com.ym.mec.biz.dal.enums.IndexDataType;
import com.ym.mec.biz.dal.enums.JobNatureEnum;
import com.ym.mec.biz.dal.enums.PaymentStatusEnum;
import com.ym.mec.common.constant.CommonConstants;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.service.impl.BaseServiceImpl;
import com.ym.mec.util.date.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.ym.mec.biz.dal.entity.IndexBaseMonthData;
import com.ym.mec.biz.service.IndexBaseMonthDataService;
import com.ym.mec.biz.dal.dao.IndexBaseMonthDataDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class IndexBaseMonthDataServiceImpl extends BaseServiceImpl<Long, IndexBaseMonthData> implements IndexBaseMonthDataService {
	
	@Autowired
	private IndexBaseMonthDataDao indexBaseMonthDataDao;
	@Autowired
	private OrganizationDao organizationDao;

	private static ThreadLocal<Set<Integer>> organIds = new ThreadLocal<Set<Integer>>(){
		@Override
		protected Set<Integer> initialValue() {
			return new HashSet<>();
		}
	};

	@Override
	public BaseDAO<Long, IndexBaseMonthData> getDAO() {
		return indexBaseMonthDataDao;
	}

	@Override
	public List<IndexBaseDto> getIndexBaseData(String dataTypesStr, String organIdsStr, Integer year) {
		List<IndexBaseDto> result = new ArrayList<>();

		DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM");
		LocalDate nowDate = LocalDate.now();
		nowDate = nowDate.withDayOfMonth(1);
		Date currentMonth = Date.from(nowDate.atStartOfDay(DateUtil.zoneId).toInstant());

		LocalDate startDate = LocalDate.now();
		if(Objects.nonNull(year)){
			startDate = startDate.withYear(year);
		}
		startDate = startDate.withMonth(1);
		startDate = startDate.withDayOfMonth(1);

		LocalDate endDate = startDate.withMonth(12);

		Set<Integer> organIds = null;
		if(StringUtils.isNotBlank(organIdsStr)){
			organIds = Arrays.stream(organIdsStr.split(",")).map(Integer::new).collect(Collectors.toSet());
		}
		Set<String> dataTypes = null;
		if(StringUtils.isNotBlank(dataTypesStr)){
			dataTypes = Arrays.stream(dataTypesStr.split(",")).collect(Collectors.toSet());
		}

		List<IndexBaseMonthData> indexBaseDatas = indexBaseMonthDataDao.getIndexBaseData(organIds, dataTypes, df.format(startDate), df.format(endDate));
		if(Objects.isNull(indexBaseDatas)){
			indexBaseDatas = new ArrayList<>();
		}

		Map<IndexDataType, List<IndexBaseMonthData>> typeDateMap = indexBaseDatas.stream().collect(Collectors.groupingBy(IndexBaseMonthData::getDataType));
		for (Map.Entry<IndexDataType, List<IndexBaseMonthData>> typeDateMapEntry : typeDateMap.entrySet()) {
			Set<String> hasMonths = typeDateMapEntry.getValue().stream().map(d -> DateUtil.dateToString(d.getMonth(), "yyyy-MM-dd")).collect(Collectors.toSet());
			LocalDate currentMonthDate = startDate;
			while (currentMonthDate.compareTo(endDate)<=0){
				if(hasMonths.contains(currentMonthDate.toString())){
					currentMonthDate = currentMonthDate.plusMonths(1);
					continue;
				}
				IndexBaseMonthData indexBaseMonthData = new IndexBaseMonthData();
				indexBaseMonthData.setMonth(Date.from(currentMonthDate.atStartOfDay(DateUtil.zoneId).toInstant()));
				indexBaseMonthData.setTotalNum(BigDecimal.ZERO);
				indexBaseMonthData.setActivateNum(BigDecimal.ZERO);
				indexBaseMonthData.setPercent(BigDecimal.ZERO);
				indexBaseMonthData.setDataType(typeDateMapEntry.getKey());
				typeDateMapEntry.getValue().add(indexBaseMonthData);
				currentMonthDate = currentMonthDate.plusMonths(1);
			}
			typeDateMapEntry.getValue().sort(Comparator.comparing(IndexBaseMonthData::getMonth));
			if(IndexDataType.ACTIVATION_RATE.equals(typeDateMapEntry.getKey())||IndexDataType.HOMEWORK_CREATE_RATE.equals(typeDateMapEntry.getKey())
					||IndexDataType.HOMEWORK_SUBMIT_RATE.equals(typeDateMapEntry.getKey())||IndexDataType.HOMEWORK_SUBMIT_RATE.equals(typeDateMapEntry.getKey())
					||IndexDataType.STUDENT_CONVERSION.equals(typeDateMapEntry.getKey())){
				for (IndexBaseMonthData indexBaseMonthData : typeDateMapEntry.getValue()) {
					if(indexBaseMonthData.getTotalNum().compareTo(BigDecimal.ZERO)==0){
						indexBaseMonthData.setPercent(BigDecimal.ZERO);
						continue;
					}
					indexBaseMonthData.setPercent(indexBaseMonthData.getActivateNum().divide(indexBaseMonthData.getTotalNum(), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(100)).setScale(CommonConstants.DECIMAL_FINAL_PLACE, BigDecimal.ROUND_DOWN));
				}
			}
			IndexBaseDto indexBaseData = new IndexBaseDto(typeDateMapEntry.getKey(),typeDateMapEntry.getKey().getMsg());
			indexBaseData.setIndexMonthData(typeDateMapEntry.getValue(), currentMonth);
			result.add(indexBaseData);
		}
		countStudentConvertData(result, currentMonth);
		return result;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Map<String, List<IndexBaseDto>> indexBaseDataTask(String month) {
		Map<String, List<IndexBaseDto>> result = new HashMap<>();

		DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM");
		LocalDate nowDate = LocalDate.now();

		List<Organization> allOrgans = organizationDao.findAllOrgans();
		if(CollectionUtils.isEmpty(this.organIds.get())){
			this.organIds.get().clear();
			this.organIds.get().addAll(allOrgans.stream().map(Organization::getId).collect(Collectors.toSet()));
		}

		if(StringUtils.isBlank(month)){
			month = df.format(nowDate);
		}

		//运营数据
		saveData(indexBaseMonthDataDao.getSchoolData(month), month, IndexDataType.SCHOOL);
		saveData(indexBaseMonthDataDao.getMusicData(month), month, IndexDataType.MUSIC_GROUP_NUM);
		saveData(indexBaseMonthDataDao.getMusicStudentData(month, null), month, IndexDataType.MUSIC_GROUP_STUDENT);
		saveData(indexBaseMonthDataDao.getOtherStudentData(month), month, IndexDataType.OTHER_STUDENT);

		//业务数据
		saveData(indexBaseMonthDataDao.getStudentSignUpData(month), month, IndexDataType.ACTIVATION_RATE);
		saveData(indexBaseMonthDataDao.getHomeworkData(month, null), month, IndexDataType.HOMEWORK_CREATE_RATE);
		saveData(indexBaseMonthDataDao.getHomeworkData(month, "submit"), month, IndexDataType.HOMEWORK_SUBMIT_RATE);
		saveData(indexBaseMonthDataDao.getHomeworkData(month, "comment"), month, IndexDataType.HOMEWORK_COMMENT_RATE);

		//经营数据
		saveData(null, month, IndexDataType.SHOULD_INCOME_MONEY);
		saveData(null, month, IndexDataType.ANTICIPATED_INCOME_MONEY);
		saveData(null, month, IndexDataType.SHOULD_EXPEND_MONEY);
		saveData(null, month, IndexDataType.ANTICIPATED_EXPEND_MONEY);
		saveData(null, month, IndexDataType.REVENUE_MONEY);

		//人事数据
		saveData(indexBaseMonthDataDao.getTeacherData(month, null, null), month, IndexDataType.TEACHER_NUM);
		saveData(indexBaseMonthDataDao.getTeacherData(month, JobNatureEnum.FULL_TIME, null), month, IndexDataType.FULL_TIME_NUM);
		saveData(indexBaseMonthDataDao.getTeacherData(month, JobNatureEnum.PART_TIME, null), month, IndexDataType.PART_TIME_NUM);
		saveData(indexBaseMonthDataDao.getTeacherData(month, null, true), month, IndexDataType.DIMISSION_NUM);

		//学员变动
		saveData(indexBaseMonthDataDao.getMusicStudentData(month, "ALL"), month, IndexDataType.NEWLY_STUDENT_NUM);
		saveData(indexBaseMonthDataDao.getMusicStudentData(month, "QUIT"), month, IndexDataType.QUIT_MUSIC_GROUP_STUDENT_NUM);
		saveData(indexBaseMonthDataDao.getMusicGroupPreRegistrationStudentData(month), month, IndexDataType.STUDENT_CONVERSION_PRE_STUDENT_NUM);
		saveData(indexBaseMonthDataDao.getMusicGroupStudentFromPreData(month, null), month, IndexDataType.STUDENT_CONVERSION_STUDENT_NUM);
		saveData(indexBaseMonthDataDao.getMusicGroupStudentFromPreData(month, PaymentStatusEnum.YES), month, IndexDataType.STUDENT_CONVERSION_PAYMENT_STUDENT_NUM);
		List<Integer> studentIds = indexBaseMonthDataDao.getMusicGroupStudentIdFromPre(month, PaymentStatusEnum.YES);
		List<IndexBaseMonthData> convertStudentNum = new ArrayList<>();
		if(!CollectionUtils.isEmpty(studentIds)){
			convertStudentNum = indexBaseMonthDataDao.getStudentConversionData(month, studentIds);
		}
		saveData(convertStudentNum, month, IndexDataType.STUDENT_CONVERSION_VIP_PRACTICE_STUDENT_NUM);

		//课程数据
		saveData(indexBaseMonthDataDao.getGroupCourseData(month, GroupType.MUSIC), month, IndexDataType.MUSIC_GROUP_COURSE);
		saveData(indexBaseMonthDataDao.getGroupCourseData(month, GroupType.VIP), month, IndexDataType.VIP_GROUP_COURSE);
		saveData(indexBaseMonthDataDao.getGroupCourseData(month, GroupType.PRACTICE), month, IndexDataType.PRACTICE_GROUP_COURSE);

		return result;
	}

	private void countStudentConvertData(List<IndexBaseDto> result, Date currentMonth){
		List<IndexBaseMonthData> convertData = new ArrayList<>();

		Map<IndexDataType, IndexBaseDto> typeDataMap = result.stream().collect(Collectors.toMap(IndexBaseDto::getDataType, i -> i, (i1, i2) -> i1));
		List<IndexBaseMonthData> preStudentNum = typeDataMap.get(IndexDataType.STUDENT_CONVERSION_PRE_STUDENT_NUM).getIndexMonthData();

		IndexBaseMonthData preStudentNumData = new IndexBaseMonthData();
		preStudentNumData.setMonth(currentMonth);
		preStudentNumData.setDataType(IndexDataType.STUDENT_CONVERSION_PRE_STUDENT_NUM);
		preStudentNumData.setTitle(IndexDataType.STUDENT_CONVERSION_PRE_STUDENT_NUM.getMsg());
		preStudentNumData.setTotalNum(preStudentNum.stream().map(IndexBaseMonthData::getTotalNum).reduce(BigDecimal.ZERO, BigDecimal::add));
		preStudentNumData.setActivateNum(preStudentNum.stream().map(IndexBaseMonthData::getActivateNum).reduce(BigDecimal.ZERO, BigDecimal::add));

		List<IndexBaseMonthData> studentNum = typeDataMap.get(IndexDataType.STUDENT_CONVERSION_STUDENT_NUM).getIndexMonthData();

		IndexBaseMonthData studentNumData = new IndexBaseMonthData();
		studentNumData.setMonth(currentMonth);
		studentNumData.setDataType(IndexDataType.STUDENT_CONVERSION_STUDENT_NUM);
		studentNumData.setTitle(IndexDataType.STUDENT_CONVERSION_STUDENT_NUM.getMsg());
		studentNumData.setTotalNum(studentNum.stream().map(IndexBaseMonthData::getTotalNum).reduce(BigDecimal.ZERO, BigDecimal::add));
		studentNumData.setActivateNum(studentNum.stream().map(IndexBaseMonthData::getActivateNum).reduce(BigDecimal.ZERO, BigDecimal::add));

		List<IndexBaseMonthData> paymentStudentNum = typeDataMap.get(IndexDataType.STUDENT_CONVERSION_PAYMENT_STUDENT_NUM).getIndexMonthData();

		IndexBaseMonthData paymentStudentNumData = new IndexBaseMonthData();
		paymentStudentNumData.setMonth(currentMonth);
		paymentStudentNumData.setDataType(IndexDataType.STUDENT_CONVERSION_PAYMENT_STUDENT_NUM);
		paymentStudentNumData.setTitle(IndexDataType.STUDENT_CONVERSION_PAYMENT_STUDENT_NUM.getMsg());
		paymentStudentNumData.setTotalNum(paymentStudentNum.stream().map(IndexBaseMonthData::getTotalNum).reduce(BigDecimal.ZERO, BigDecimal::add));
		paymentStudentNumData.setActivateNum(paymentStudentNum.stream().map(IndexBaseMonthData::getActivateNum).reduce(BigDecimal.ZERO, BigDecimal::add));

		List<IndexBaseMonthData> convertStudentNum = typeDataMap.get(IndexDataType.STUDENT_CONVERSION_VIP_PRACTICE_STUDENT_NUM).getIndexMonthData();

		IndexBaseMonthData convertStudentNumData = new IndexBaseMonthData();
		convertStudentNumData.setMonth(currentMonth);
		convertStudentNumData.setDataType(IndexDataType.STUDENT_CONVERSION_VIP_PRACTICE_STUDENT_NUM);
		convertStudentNumData.setTitle(IndexDataType.STUDENT_CONVERSION_VIP_PRACTICE_STUDENT_NUM.getMsg());
		convertStudentNumData.setTotalNum(convertStudentNum.stream().map(IndexBaseMonthData::getTotalNum).reduce(BigDecimal.ZERO, BigDecimal::add));
		convertStudentNumData.setActivateNum(convertStudentNum.stream().map(IndexBaseMonthData::getActivateNum).reduce(BigDecimal.ZERO, BigDecimal::add));

		if(preStudentNumData.getActivateNum().compareTo(BigDecimal.ZERO)==0){
			studentNumData.setPercent(BigDecimal.ZERO);
			paymentStudentNumData.setPercent(BigDecimal.ZERO);
			convertStudentNumData.setPercent(BigDecimal.ZERO);
		}else{
			studentNumData.setPercent(studentNumData.getActivateNum().divide(preStudentNumData.getActivateNum()).multiply(new BigDecimal(100)).setScale(CommonConstants.DECIMAL_FINAL_PLACE, BigDecimal.ROUND_DOWN));
			if(studentNumData.getActivateNum().compareTo(BigDecimal.ZERO)==0){
				paymentStudentNumData.setPercent(BigDecimal.ZERO);
				convertStudentNumData.setPercent(BigDecimal.ZERO);
			}else{
				paymentStudentNumData.setPercent(paymentStudentNumData.getActivateNum().divide(studentNumData.getActivateNum()).multiply(new BigDecimal(100)).setScale(CommonConstants.DECIMAL_FINAL_PLACE, BigDecimal.ROUND_DOWN));
				if(paymentStudentNumData.getActivateNum().compareTo(BigDecimal.ZERO)==0){
					convertStudentNumData.setPercent(BigDecimal.ZERO);
				}else{
					convertStudentNumData.setPercent(convertStudentNumData.getActivateNum().divide(paymentStudentNumData.getActivateNum()).multiply(new BigDecimal(100)).setScale(CommonConstants.DECIMAL_FINAL_PLACE, BigDecimal.ROUND_DOWN));
				}
			}
		}

		convertData.add(preStudentNumData);
		convertData.add(studentNumData);
		convertData.add(paymentStudentNumData);
		convertData.add(convertStudentNumData);
		IndexBaseDto indexBaseData = new IndexBaseDto(IndexDataType.STUDENT_CONVERSION, IndexDataType.STUDENT_CONVERSION.getMsg());
		indexBaseData.setIndexMonthData(convertData, currentMonth);
		indexBaseData.setPercent(convertStudentNumData.getPercent());
		result.add(indexBaseData);
	}

	/**
	 * @describe 处理并保存每月数据
	 * @author Joburgess
	 * @date 2021/1/11 0011
	 * @return void
	 */
	private void saveData(List<IndexBaseMonthData> dataList, String startMonth, IndexDataType indexDataType){
		startMonth = startMonth+"-01";
		Date date = DateUtil.stringToDate(startMonth, "yyyy-MM-dd");
		if(Objects.isNull(dataList)){
			dataList = new ArrayList<>();
		}
		Set<Integer> hasOrganIds = dataList.stream().map(IndexBaseMonthData::getOrganId).collect(Collectors.toSet());
		for (Integer organId : this.organIds.get()) {
			if(hasOrganIds.contains(organId)){
				continue;
			}
			dataList.add(new IndexBaseMonthData(date, organId));
		}
		indexBaseMonthDataDao.deleteWithMonthAndType(Arrays.asList(startMonth), indexDataType);
		indexBaseMonthDataDao.batchInsertWithDataType(dataList, indexDataType);
	}
}
