package com.yonge.cooleshow.biz.dal.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.yonge.cooleshow.auth.api.entity.SysUser;
import com.yonge.cooleshow.biz.dal.dao.MusicSheetDao;
import com.yonge.cooleshow.biz.dal.dao.SysMusicCompareRecordDao;
import com.yonge.cooleshow.biz.dal.dto.*;
import com.yonge.cooleshow.biz.dal.entity.*;
import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
import com.yonge.cooleshow.biz.dal.enums.FeatureType;
import com.yonge.cooleshow.biz.dal.enums.IndexDataType;
import com.yonge.cooleshow.biz.dal.queryInfo.SysMusicCompareRecordQueryInfo;
import com.yonge.cooleshow.biz.dal.service.*;
import com.yonge.cooleshow.biz.dal.wrapper.StatGroupWrapper;
import com.yonge.cooleshow.biz.dal.wrapper.music.MusicCompareWrapper;
import com.yonge.cooleshow.common.constant.SysConfigConstant;
import com.yonge.toolset.base.page.PageInfo;
import com.yonge.toolset.base.util.ThreadPool;
import com.yonge.toolset.mybatis.dal.BaseDAO;
import com.yonge.toolset.mybatis.service.impl.BaseServiceImpl;
import com.yonge.toolset.utils.collection.MapUtil;
import com.yonge.toolset.utils.date.DateUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SysMusicCompareRecordServiceImpl extends BaseServiceImpl<Long, SysMusicCompareRecord>  implements SysMusicCompareRecordService {

	private final static Logger log = LoggerFactory.getLogger(SysMusicCompareRecordServiceImpl.class);
	@Autowired
	private SysMusicCompareRecordDao sysMusicCompareRecordDao;
	@Autowired
	private SysConfigService sysConfigService;
	@Autowired
	private MusicSheetDao musicSheetDao;
	@Autowired
	private MusicCompareRecordStatService musicCompareRecordStatService;
	@Autowired
	private TeacherService teacherService;
	@Autowired
	private StudentService studentService;
	@Autowired
	private SysUserService sysUserService;
	@Autowired
	private SysMusicCompareWeekDataService sysMusicCompareWeekDataService;

    @Autowired
    private UserMusicService userMusicService;

	@Override
	public BaseDAO<Long, SysMusicCompareRecord> getDAO() {
		return sysMusicCompareRecordDao;
	}

	@Override
	public long insert(SysMusicCompareRecord bean) {
		Long tenant = sysUserService.getTenantByClient(bean.getUserId(), bean.getClientId());
		bean.setTenantId(tenant);
		// 保存评测记录
		long record = super.insert(bean);

		if (record > 0) {
			// 生成曲目评测统计数据
			saveMusicCompareRecordStatInfo(bean);
		}

		return record;
	}

	/**
	 * 生成曲目统计数据
	 * @param record SysMusicCompareRecord
	 */
	private void saveMusicCompareRecordStatInfo(SysMusicCompareRecord record) {

		ThreadPool.getExecutor().submit(() -> {
			try {

				// 统计单曲练习时长，频率；评测时长、频率
				ClientEnum clientType = Optional.of(ClientEnum.valueOf(record.getClientId().toUpperCase()))
						.orElse(ClientEnum.STUDENT);

				// 单曲统计
				MusicCompareWrapper.RecordInfo musicStatRecord = MusicCompareWrapper.RecordInfo.builder()
						.musicSheetId(record.getMusicSheetId())
						.clientId(clientType)
						.userId(0L)
						.recentTime(DateTime.now().getMillis())
						.build();

				// 用户单曲统计
				MusicCompareWrapper.RecordInfo userStatRecord = MusicCompareWrapper.RecordInfo.builder()
						.musicSheetId(record.getMusicSheetId())
						.clientId(clientType)
						.userId(record.getUserId())
						.recentTime(DateTime.now().getMillis())
						.build();

				// 老师、学生练习、评测
				MusicCompareWrapper.UserMusicCompareRecord compareRecord = MusicCompareWrapper.UserMusicCompareRecord
						.builder().build();

				// 按练习、评测分别进行统计
				Lists.newArrayList(FeatureType.values()).parallelStream().forEach(dataType -> {

					MusicCompareWrapper.RecordQueryInfo queryInfo = MusicCompareWrapper.RecordQueryInfo.builder()
							.clientId(record.getClientId())
							.musicSheetId(record.getMusicSheetId())
							.feature(dataType)
							.build();

					if (FeatureType.CLOUD_STUDY_EVALUATION == dataType) {
						// 只统计生成评测报告数据
						queryInfo.setScoreData(true);
					}

					// 曲目统计
					List<StatGroupWrapper> musicWrappers = sysMusicCompareRecordDao.selectMusicCompareRecordStatInfo(queryInfo);

					// 用户曲目统计
					queryInfo.setUserId(record.getUserId());

					List<StatGroupWrapper> userMusicWrappers = sysMusicCompareRecordDao.selectMusicCompareRecordStatInfo(queryInfo);

					// 老师、学生统计汇总时长, 删除按曲目统计限制
					queryInfo.setMusicSheetId(null);
					List<StatGroupWrapper> wrappers = sysMusicCompareRecordDao.selectMusicCompareRecordStatInfo(queryInfo);

					switch (dataType) {
						case CLOUD_STUDY_TRAIN: // 练习
							{
								// 老师、学生统计汇总时长
								if (CollectionUtils.isNotEmpty(wrappers)) {
									StatGroupWrapper wrapper = wrappers.get(0);

									compareRecord.setTrainTime(wrapper.getNumber().longValue() / 60);
								}
								// 先统计练习曲目
								if (CollectionUtils.isNotEmpty(musicWrappers)) {

									StatGroupWrapper wrapper = musicWrappers.get(0);

									musicStatRecord.trainDuration(wrapper.getNumber().longValue())
											.trainFrequency(wrapper.getTotal().longValue());
								}

								// 在按用户统计
								if (CollectionUtils.isNotEmpty(userMusicWrappers)) {
									StatGroupWrapper wrapper = userMusicWrappers.get(0);

									userStatRecord.trainDuration(wrapper.getNumber().longValue())
											.trainFrequency(wrapper.getTotal().longValue());
								}
							}
							break;
						case CLOUD_STUDY_EVALUATION: // 评测
							{
								// 老师、学生统计汇总时长
								if (CollectionUtils.isNotEmpty(wrappers)) {
									StatGroupWrapper wrapper = wrappers.get(0);

									compareRecord.setEvaluateTime(wrapper.getNumber().longValue() / 60);
								}
								// 先统计评测曲目
								if (CollectionUtils.isNotEmpty(musicWrappers)) {

									StatGroupWrapper wrapper = musicWrappers.get(0);

									musicStatRecord.evaluateDuration(wrapper.getNumber().longValue())
											.evaluateFrequency(wrapper.getTotal().longValue());
								}

								// 在按用户统计
								if (CollectionUtils.isNotEmpty(userMusicWrappers)) {
									StatGroupWrapper wrapper = userMusicWrappers.get(0);

									userStatRecord.evaluateDuration(wrapper.getNumber().longValue())
											.evaluateFrequency(wrapper.getTotal().longValue());
								}
							}
							break;
						default:
							break;
					}
				});

				List<MusicCompareWrapper.RecordInfo> recordInfos = Lists.newArrayList(musicStatRecord, userStatRecord);

				musicCompareRecordStatService.batchUpdateMusicCompareRecordStat(recordInfos);

				// 更新用户曲目统计
				if (Objects.nonNull(compareRecord.getTrainTime())
						|| Objects.nonNull(compareRecord.getEvaluateTime())) {

					switch (clientType) {
						case TEACHER:
							teacherService.lambdaUpdate()
									.eq(Teacher::getUserId, record.getUserId())
									.update(JSON.parseObject(compareRecord.jsonString(), Teacher.class));
							break;
						case STUDENT:
							studentService.lambdaUpdate()
									.eq(Student::getUserId, record.getUserId())
									.update(JSON.parseObject(compareRecord.jsonString(), Student.class));
							break;
						default:
							break;
					}

				}
				//按周统计
				sysMusicCompareWeekDataService.updateUserWeekTrainData(record.getUserId(), LocalDate.now().with(DateUtil.weekFields.dayOfWeek(), DayOfWeek.MONDAY.getValue()));

			} catch (Exception e) {
				log.error("MusicCompareRecordStat userId={}, musicId={}", record.getUserId(), record.getMusicSheetId(), e);
			}
		});
	}

	@Override
	public SysMusicCompareRecordDao getDao() {
		return sysMusicCompareRecordDao;
	}

	@Override
	@Transactional
	public void saveMusicCompareData(SysMusicCompareRecord sysMusicCompareRecord) {
		Long tenant = sysUserService.getTenantByClient(sysMusicCompareRecord.getUserId(), sysMusicCompareRecord.getClientId());
		sysMusicCompareRecord.setTenantId(tenant);
		int record = sysMusicCompareRecordDao.update(sysMusicCompareRecord);

		if (record > 0) {
			SysMusicCompareRecord info = sysMusicCompareRecordDao.get(sysMusicCompareRecord.getId());

			if (Objects.nonNull(info)) {

				// 生成曲目评测统计数据
				saveMusicCompareRecordStatInfo(info);
			}
		}
	}

	@Override
	public Object getLastEvaluationMusicalNotesPlayStats(Long userId, Long recordId) {
		SysMusicCompareRecord userLastEvaluationData;
		if(Objects.nonNull(recordId)){
			userLastEvaluationData = sysMusicCompareRecordDao.get(recordId);
		}else{
			userLastEvaluationData = sysMusicCompareRecordDao.getUserLastEvaluationData(userId);
		}
		if(Objects.isNull(userLastEvaluationData)){
			return null;
		}
		if(StringUtils.isBlank(userLastEvaluationData.getScoreData())){
			return null;
		}

		JSONObject jsonObject = JSON.parseObject(userLastEvaluationData.getScoreData());
		jsonObject.put("recordId", userLastEvaluationData.getId());
		jsonObject.put("score", userLastEvaluationData.getScore());
		jsonObject.put("cadence", userLastEvaluationData.getCadence());
		jsonObject.put("intonation", userLastEvaluationData.getIntonation());
		jsonObject.put("integrity", userLastEvaluationData.getIntegrity());
		jsonObject.put("heardLevel", userLastEvaluationData.getHeardLevel());
        jsonObject.put("videoFilePath", StringUtils.isNotBlank(userLastEvaluationData.getVideoFilePath())?userLastEvaluationData.getVideoFilePath():userLastEvaluationData.getRecordFilePath());
        jsonObject.put("partIndex", userLastEvaluationData.getPartIndex());
		jsonObject.put("customConfiguration", userLastEvaluationData.getCustomConfiguration());
		if(userLastEvaluationData.getCreateTime() == null) {
			jsonObject.put("expire", 1);
		} else {
			LocalDateTime localDateTime = userLastEvaluationData.getCreateTime()
																.toInstant()
																.atZone(ZoneId.systemDefault())
																.toLocalDateTime();
			if (localDateTime.plusDays(Long.parseLong(getExpireTime())).compareTo(LocalDateTime.now()) <0) {
				jsonObject.put("expire", 1);
			} else {
				jsonObject.put("expire", 0);
			}
		}

		MusicSheet sysMusicScore = musicSheetDao.selectById(userLastEvaluationData.getMusicSheetId());
		if(Objects.nonNull(sysMusicScore)){
			jsonObject.put("sysMusicScoreName", sysMusicScore.getMusicSheetName());
		}


        // 上传过作品，用作品视频路径覆盖上传视频路径
        // ID集合
        Map<Long, UserMusic> userMusicMap = userMusicService.getMapByMusicPracticeRecordIds(Lists.newArrayList(recordId));

        UserMusic userMusic = userMusicMap.get(recordId);
        if (Objects.nonNull(userMusic)) {
            jsonObject.put("videoFilePath", userMusic.getVideoUrl());
        }
		return jsonObject;
	}

	@Override
	public Map<String, Object> studentTrainData(SysMusicCompareRecordQueryInfo.TeacherCompareRecordQueryInfo queryInfo) {
		StudentTrainOverviewDto userTrainOverView = new StudentTrainOverviewDto();

		List<StudentTrainChartDto> userTrainChartData = new ArrayList<>();
		if(queryInfo.getPage()==1){
			userTrainOverView = sysMusicCompareRecordDao.getUserTrainOverView(queryInfo.getUserId(),queryInfo.getClientId());
			userTrainChartData = sysMusicCompareRecordDao.getUserTrainChartData(queryInfo.getUserId(), queryInfo.getStartTime(),
																				queryInfo.getEndTime(),queryInfo.getClientId());
		}

		if(CollectionUtils.isEmpty(userTrainChartData) || userTrainChartData.size() < 7){
			Set<LocalDate> trainDates = userTrainChartData.stream().map(StudentTrainChartDto::getTrainDate).collect(Collectors.toSet());
			LocalDate startLocalDate = LocalDate.parse(queryInfo.getStartTime(), DateUtil.dateFormatter);
			for (int i = 1; i <= 7; i++) {
				if(!trainDates.contains(startLocalDate)){
					userTrainChartData.add(new StudentTrainChartDto(0, 0, startLocalDate));
				}
				startLocalDate=startLocalDate.plusDays(1);
			}
		}

		PageInfo<SysMusicCompareRecord> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
		Map<String, Object> params = new HashMap<>();
		queryInfo.setFeatureType(FeatureType.CLOUD_STUDY_EVALUATION);
		MapUtil.populateMap(params, queryInfo);

		params.put("expireTime",getExpireTime());
		List<SysMusicCompareRecord> dataList = null;
		int count = this.findCount(params);
		if (count > 0) {
			pageInfo.setTotal(count);
			params.put("offset", pageInfo.getOffset());
			dataList = this.getDAO().queryPage(params);
		}
		if (count == 0) {
			dataList = new ArrayList<>();
		}
		pageInfo.setRows(dataList);
		Map<String, Object> result = new HashMap<>();
		userTrainOverView.setTrainTime(userTrainOverView.getTrainTime()/60);
		if(userTrainOverView.getTrainTime() > 0 && userTrainOverView.getTrainDays() > 0){
			userTrainOverView.setAvgTrainTime(userTrainOverView.getTrainTime() / userTrainOverView.getTrainDays());
		}
		result.put("userTrainOverView",userTrainOverView);

		List<IndexBaseDto> userTrainChartResult = new ArrayList<>();
		List<IndexBaseMonthData> trainNumData = new ArrayList<>();
		List<IndexBaseMonthData> trainTimeData = new ArrayList<>();

		for (StudentTrainChartDto data : userTrainChartData) {
			Date trainDate = Date.from(data.getTrainDate().atStartOfDay(DateUtil.zoneId).toInstant());
			trainTimeData.add(new IndexBaseMonthData(trainDate, null, new BigDecimal(data.getTrainTime()/60)));
			trainNumData.add(new IndexBaseMonthData(trainDate, null, new BigDecimal(data.getTrainNum())));
		}

		trainNumData.sort(Comparator.comparing(IndexBaseMonthData::getMonth));
		trainTimeData.sort(Comparator.comparing(IndexBaseMonthData::getMonth));

		userTrainChartResult.add(new IndexBaseDto(IndexDataType.CLOUD_STUDY_TRAIN_TIME, trainTimeData));
		userTrainChartResult.add(new IndexBaseDto(IndexDataType.CLOUD_STUDY_TRAIN_NUM, trainNumData));

		result.put("userTrainChartData", userTrainChartResult);
		result.put("detail",pageInfo);
		return result;
	}

	@Override
	public PageInfo<SysMusicCompareRecordQueryInfo.WechatCompareRecordPageDto> weChatStudentTrainData(SysMusicCompareRecordQueryInfo.WechatCompareRecordQueryInfo queryInfo) {
		PageInfo<SysMusicCompareRecordQueryInfo.WechatCompareRecordPageDto> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
		Map<String, Object> params = new HashMap<>();
		MapUtil.populateMap(params, queryInfo);
		List<SysMusicCompareRecordQueryInfo.WechatCompareRecordPageDto> dataList = null;
		int count = sysMusicCompareRecordDao.countWeChatStudentTrainData(params);
		if (count > 0) {
			pageInfo.setTotal(count);
			params.put("offset", pageInfo.getOffset());
			dataList = sysMusicCompareRecordDao.queryWeChatStudentTrainData(params);
		}
		if (count == 0) {
			dataList = new ArrayList<>();
		}
		pageInfo.setRows(dataList);
		return pageInfo;
	}

	@Override
	public PageInfo<SysMusicCompareRecord> queryStudentTrainData(SysMusicCompareRecordQueryInfo.TeacherCompareRecordQueryInfo queryInfo) {
		PageInfo<SysMusicCompareRecord> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
		Map<String, Object> params = new HashMap<>();
		MapUtil.populateMap(params, queryInfo);

		List<SysMusicCompareRecord> dataList = null;
		int count = this.findCount(params);
		if (count > 0) {
			pageInfo.setTotal(count);
			params.put("offset", pageInfo.getOffset());
			dataList = this.getDAO().queryPage(params);
		}
		if (count == 0) {
			dataList = new ArrayList<>();
		}
		pageInfo.setRows(dataList);
		return pageInfo;
	}

    @Override
    public PageInfo<SysMusicCompareRecordQueryInfo.StudentMusicCompareRecord> queryStudentTrain(SysMusicCompareRecordQueryInfo.StudentMusicCompareRecordQuery query) {
		PageInfo<SysMusicCompareRecordQueryInfo.StudentMusicCompareRecord> pageInfo = new PageInfo<>(query.getPage(), query.getRows());
		Map<String, Object> params = new HashMap<>();
		MapUtil.populateMap(params, query);

		List<SysMusicCompareRecordQueryInfo.StudentMusicCompareRecord> dataList = null;
		int count = sysMusicCompareRecordDao.countStudentTrain(params);
		if (count > 0) {
			pageInfo.setTotal(count);
			params.put("offset", pageInfo.getOffset());
			dataList = sysMusicCompareRecordDao.queryStudentTrain(params);
		}
		if (count == 0) {
			dataList = new ArrayList<>();
		}
		pageInfo.setRows(dataList);
		return pageInfo;
    }

	@Override
	public SysMusicCompareRecordQueryInfo.StudentMusicCompareRecordStat studentMusicCompareRecordStat(SysMusicCompareRecordQueryInfo.StudentMusicCompareRecordQuery query) {
		//统计训练时长
		SysMusicCompareRecordQueryInfo.StudentMusicCompareRecordStat vo = sysMusicCompareRecordDao.studentMusicCompareRecordStat(query);
		//获取学员声部
		List<Subject> subjects = studentService.querySubject(query.getUserId());
		if(CollectionUtils.isNotEmpty(subjects)){
			vo.setSubjectName(subjects.stream().map(e->e.getName()).collect(Collectors.joining()));
		}
		return vo;
	}

	@Override
	public SysMusicCompareRecordQueryInfo.StudentMusicCompareRecordDto studentTrainStat(SysMusicCompareRecordQueryInfo.StudentMusicCompareRecordQuery query) {
		SysMusicCompareRecordQueryInfo.StudentMusicCompareRecordDto dto = sysMusicCompareRecordDao.studentTrainStat(query);
		if(Objects.nonNull(dto) && dto.getTrainTime() > 0 && dto.getTrainDays() > 0){
			dto.setAvgTrainTime((int)(dto.getTrainTime() / dto.getTrainDays().doubleValue() * 100));
		}
		return sysMusicCompareRecordDao.studentTrainStat(query);
	}

    @Override
    public SysMusicCompareRecordQueryInfo.StatDto rankingList(SysMusicCompareRecordQueryInfo.StudentCompareRecordQueryInfo queryInfo) {
		SysMusicCompareRecordQueryInfo.StatDto result = new SysMusicCompareRecordQueryInfo.StatDto();
		SysMusicCompareRecordQueryInfo.MusicCompareRankingDto head = null;
		List<SysMusicCompareRecordQueryInfo.MusicCompareRankingDto> userTrainStat = sysMusicCompareWeekDataService.getDao().queryUserTrainStat(queryInfo.getStartTime(),
				queryInfo.getOrderType(),
				Objects.isNull(queryInfo.getHeardLevel())?null:queryInfo.getHeardLevel().getCode(),
				queryInfo.getTenantId());
		List<SysMusicCompareRecordQueryInfo.MusicCompareRankingDto> detail = new ArrayList<>();

		List<Long> studentIds = userTrainStat.stream().limit(10).
				map(SysMusicCompareRecordQueryInfo.MusicCompareRankingDto::getUserId).collect(Collectors.toList());
		Map<Integer, String> studentSubjectMap = new HashMap<>();
		if(!org.springframework.util.CollectionUtils.isEmpty(studentIds)){
			List<Map<Integer, String>> studentSubjectMapList = studentService.getDao().getStudentSubjectMapList(studentIds);
			studentSubjectMap = MapUtil.convertIntegerMap(studentSubjectMapList);
		}

		int rankNum = 0;
		for (int i = 0; i < userTrainStat.size(); i++) {
			if(userTrainStat.get(i).getUserId().equals(queryInfo.getUserId())){
				head = userTrainStat.get(i);
			}
			userTrainStat.get(i).setTrainTime(userTrainStat.get(i).getTrainTime()/60);
//			if(userTrainStat.get(i).getVipUser() <= 0){
//				continue;
//			}
			userTrainStat.get(i).setRankNum(++rankNum);
			if(studentSubjectMap.containsKey(userTrainStat.get(i).getUserId())){
				userTrainStat.get(i).setSubjectName(studentSubjectMap.get(userTrainStat.get(i).getUserId()));
			}
			if(detail.size()<10){
				detail.add(userTrainStat.get(i));
			}
		}
		Map<String,Object> params = new HashMap<String, Object>(1);
		params.put("tenantId", queryInfo.getTenantId());
		if(Objects.isNull(head)){
			head = new SysMusicCompareRecordQueryInfo.MusicCompareRankingDto();
			BasicUserInfo basicUserInfo = teacherService.getDao().getBasicUserInfo(queryInfo.getUserId());
			head.setUserId(basicUserInfo.getUserId());
			head.setAvatar(basicUserInfo.getAvatar());
			head.setStudentName(basicUserInfo.getUsername());
			List<Subject> subjects = teacherService.getDao().querySubject(basicUserInfo.getUserId());
			if(CollectionUtils.isNotEmpty(subjects)){
				head.setSubjectName(subjects.stream().map(e->e.getName()).collect(Collectors.joining()));
			}
		}
		head.setStudentNum(studentService.getDao().countStudentsWithTenant(params));
		result.setHead(head);
		detail.sort(Comparator.comparing(SysMusicCompareRecordQueryInfo.MusicCompareRankingDto::getRankNum).
				thenComparing(SysMusicCompareRecordQueryInfo.MusicCompareRankingDto::getUserId));
		result.setDetail(detail);
		return result;
    }

    @Override
    public Map<Long, SysMusicCompareRecord> getMapByIds(List<Long> musicPracticeRecordIds) {
        if (CollectionUtils.isEmpty(musicPracticeRecordIds)) {
            return Collections.emptyMap();
        }
        List<SysMusicCompareRecord> list = sysMusicCompareRecordDao.selectByIds(musicPracticeRecordIds);

        return list.stream().collect(Collectors.toMap(SysMusicCompareRecord::getId, o ->o));
    }


    private String getExpireTime() {

		String homeworkExpireTime = sysConfigService.findConfigValue(SysConfigConstant.HOMEWORK_EXPIRE_TIME);
		if (org.springframework.util.StringUtils.isEmpty(homeworkExpireTime)) {
			return "9999";
		}
		return homeworkExpireTime;
	}
}
