package com.ylcm.sys.service.impl;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;

import javax.annotation.Resource;

import com.ylcm.sys.common.Util;
import com.ylcm.sys.domain.*;
import com.ylcm.sys.dto.AdTvQueryDTO;
import com.ylcm.sys.mapper.AdSpecialMapper;
import com.ylcm.sys.service.*;
import com.ylcm.sys.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ylcm.sys.common.EnumHelperUtil;
import com.ylcm.sys.common.ProjectUtil;
import com.ylcm.sys.enums.AdTheatreTimeEnum;
import com.ylcm.sys.enums.MediaRankEnum;
import com.ylcm.sys.form.AdTheatreAnalysisDetailQueryForm;
import com.ylcm.sys.form.AdTheatreAnalysisQueryForm;
import com.ylcm.sys.mapper.AdTheatreMapper;
import com.ylcm.sys.mapper.AdTheatreTvStatMapper;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 关联电视剧数据 服务实现类
 * </p>
 *
 * @author admin
 * @since 2020-07-01
 */
@Slf4j
@Service
public class AdTheatreTvStatServiceImpl extends ServiceImpl<AdTheatreTvStatMapper, AdTheatreTvStat> implements AdTheatreTvStatService {

	@Resource
	private AdTheatreMapper adTheatreMapper;

	@Resource
	private AdMediaService adMediaService;

	@Resource
	private AdTheatreTvStatService adTheatreTvStatService;

	@Resource
	private AdTheatreTvService adTheatreTvService;

	@Resource
	private AdSpecialMapper adSpecialMapper;

	@Resource
	private TaskExecutor taskExecutor;
	@Resource
	private AdTvService adTvService;

	@Resource
	private AdTheatreService adTheatreService;

	/**
	 * 计算 某个频道的收视率
	 * @param mediaName
	 * @param theatreTime
	 * @param startDate
	 * @param endDate
	 * @return
	 */
	@Cacheable(value = "TheatreAnalysis",keyGenerator = "cacheKeyGenerator")
	@Override
	public AdTheatreTvStat statByMedia(String mediaName, Integer theatreTime, String startDate, String endDate) {
		return baseMapper.statByMedia(mediaName, theatreTime, startDate, endDate);
	}

	@Cacheable(value = "TheatreAnalysis",keyGenerator = "cacheKeyGenerator")
	@Override
	public IPage<AdTheatreAnalysisVO> getPage(AdTheatreAnalysisQueryForm queryForm) {
		if(StringUtils.isNotBlank(queryForm.getName())) {
			queryForm.setName(queryForm.getName().trim());
		}
		IPage<AdTheatreTvStat> adTheatreTvs = baseMapper.getPage(new Page<AdTheatreTvStat>(queryForm.getPageNo(), queryForm.getPageSize()), queryForm);
		IPage<AdTheatreAnalysisVO> analysisPage = new Page<AdTheatreAnalysisVO>();
		BeanUtils.copyProperties(adTheatreTvs, analysisPage);
		List<AdTheatreAnalysisVO> analysisVOs = Collections.synchronizedList(new ArrayList<AdTheatreAnalysisVO>());
		if (CollectionUtils.isNotEmpty(adTheatreTvs.getRecords())) {

			CompletableFuture[] completableFutures = adTheatreTvs.getRecords().stream().map(adTheatreTvStat -> CompletableFuture.runAsync(() -> {
				AdTheatreAnalysisVO adTheatreAnalysisVO = new AdTheatreAnalysisVO();
				BeanUtils.copyProperties(adTheatreTvStat, adTheatreAnalysisVO);

				AdTheatreTimeEnum adTheatreTimeEnum = EnumHelperUtil.getOneByCode(AdTheatreTimeEnum.class, adTheatreTvStat.getTheatreTime() + "");
				adTheatreAnalysisVO.setTheatreTimeName(adTheatreTimeEnum.getName());

				MediaRankEnum mediaRankEnum = EnumHelperUtil.getOneByCode(MediaRankEnum.class, adTheatreTvStat.getChannelSystem() + "");
				adTheatreAnalysisVO.setChannelSystem(mediaRankEnum.getName());

				adTheatreAnalysisVO.setRating29(Util.newScale(adTheatreAnalysisVO.getRating29(), 3));

				// 获取频道收视率
				List<LocalDate> seasons = ProjectUtil.getLocalDateByString(adTheatreTvStat.getSeason());
				AdTheatreTvStat mediaStat = adTheatreTvStatService.statByMedia(adTheatreTvStat.getMediaName(), adTheatreTvStat.getTheatreTime(),
						seasons.get(0).format(DateTimeFormatter.ISO_DATE), seasons.get(1).format(DateTimeFormatter.ISO_DATE));
				adTheatreAnalysisVO.setMediaDk29(mediaStat.getDk29());
				float mediaRating29 = mediaStat.getRating29();
				// 收视率保留四位小数
				adTheatreAnalysisVO.setMediaRating29(Util.newScale(mediaRating29, 3));


				adTheatreAnalysisVO.setEffect(ProjectUtil.getMediaEffect(adTheatreAnalysisVO.getRating29(), adTheatreAnalysisVO.getMediaRating29()));

				// 剧id
				AdTv adTv = adTvService.getByName(adTheatreAnalysisVO.getName());
				adTheatreAnalysisVO.setTvId(adTv == null ? 0 : adTv.getId());

				analysisVOs.add(adTheatreAnalysisVO);
			},taskExecutor)).toArray(CompletableFuture[]::new);
			CompletableFuture.allOf(completableFutures).join();

			analysisVOs.sort(new Comparator<AdTheatreAnalysisVO>() {
				@Override
				public int compare(AdTheatreAnalysisVO o1, AdTheatreAnalysisVO o2) {
					return o2.getRating29().compareTo(o1.getRating29());
				}
			});

			analysisPage.setRecords(analysisVOs);
		}
		
		return analysisPage;
	}
	
	@Override
	public IPage<AdTheatreAnalysisDetailVO> getDetailPage(AdTheatreAnalysisDetailQueryForm queryForm) {
		AdTvQueryDTO adTvQueryDTO = AdTvQueryDTO.from(queryForm);
		LambdaQueryWrapper<AdTheatre> queryWrapper = new LambdaQueryWrapper<AdTheatre>();
		queryWrapper.eq(AdTheatre::getName, adTvQueryDTO.getName());
		queryWrapper.eq(AdTheatre::getMediaName, adTvQueryDTO.getMediaName());

		if (!adTvQueryDTO.getStartTime().equals(ProjectUtil.MORNING_STR)) {
			queryWrapper.ge(AdTheatre::getShowTime, adTvQueryDTO.getStartTime());
			queryWrapper.lt(AdTheatre::getShowTime, adTvQueryDTO.getEndTime());
		} else {
			queryWrapper.apply("( (show_time >={0} ) or (show_time < {1}))", adTvQueryDTO.getStartTime(), adTvQueryDTO.getEndTime());
		}


		queryWrapper.ge(AdTheatre::getShowDate, adTvQueryDTO.getStartDate());
		queryWrapper.le(AdTheatre::getShowDate, adTvQueryDTO.getEndDate());

		IPage<AdTheatre> adTheatreTvs = adTheatreMapper.selectPage(new Page<AdTheatre>(queryForm.getPageNo(), queryForm.getPageSize()), queryWrapper);
		IPage<AdTheatreAnalysisDetailVO> analysisPage = new Page<AdTheatreAnalysisDetailVO>();
		BeanUtils.copyProperties(adTheatreTvs, analysisPage);
		
		
		List<AdTheatreAnalysisDetailVO> analysisVOs = new ArrayList<AdTheatreAnalysisDetailVO>();
		if (CollectionUtils.isNotEmpty(adTheatreTvs.getRecords())) {

			CompletableFuture[] completableFutures = adTheatreTvs.getRecords().stream().map(adTheatre -> CompletableFuture.runAsync(() -> {
				AdTheatreAnalysisDetailVO detailVO = new AdTheatreAnalysisDetailVO();
				BeanUtils.copyProperties(adTheatre, detailVO);

				detailVO.setProductName(adTheatre.getTypeName());
				detailVO.setJinxian(adTheatre.getJinxian() != null ? adTheatre.getJinxian().toString() : "");

				// 省网数据
				AdTheatreTv adTheatreTv = adTheatreTvService.getByTheatreId(adTheatre.getId());
				if (adTheatreTv != null) {
					BeanUtils.copyProperties(adTheatreTv, detailVO);
				}
				detailVO.setShowDuration(adTheatre.getDuration() == null ? "" :adTheatre.getDuration().toString());

				analysisVOs.add(detailVO);
			},taskExecutor)).toArray(CompletableFuture[]::new);
			CompletableFuture.allOf(completableFutures).join();

			analysisVOs.sort(new Comparator<AdTheatreAnalysisDetailVO>() {
				@Override
				public int compare(AdTheatreAnalysisDetailVO o1, AdTheatreAnalysisDetailVO o2) {
					return o1.getMediaNum().compareTo(o2.getMediaNum());
				}
			});
			analysisPage.setRecords(analysisVOs);
		}
		
		return analysisPage;
	}

	private void saveOrUpd(AdTheatreTvStat adTheatreTvStat) {
		LambdaQueryWrapper<AdTheatreTvStat> lambdaQueryWrapper = new LambdaQueryWrapper<AdTheatreTvStat>()
				.eq(AdTheatreTvStat::getName, adTheatreTvStat.getName())
				.eq(AdTheatreTvStat::getMediaName, adTheatreTvStat.getMediaName())
				.eq(AdTheatreTvStat::getTheatreTime, adTheatreTvStat.getTheatreTime())
				.eq(AdTheatreTvStat::getShowDate, adTheatreTvStat.getShowDate());
		AdTheatreTvStat domainInDb = baseMapper.selectOne(lambdaQueryWrapper);
		if (domainInDb == null) {
			baseMapper.insert(adTheatreTvStat);
		} else {
			Integer id = domainInDb.getId();
			BeanUtils.copyProperties(adTheatreTvStat, domainInDb);
			domainInDb.setId(id);
			baseMapper.updateById(domainInDb);
		}
	}

	private void delete(AdTheatre adTheatre, Integer theatreTime) {
		LambdaQueryWrapper<AdTheatreTvStat> lambdaQueryWrapper = new LambdaQueryWrapper<AdTheatreTvStat>()
				.eq(AdTheatreTvStat::getName, adTheatre.getName())
				.eq(AdTheatreTvStat::getMediaName, adTheatre.getMediaName())
				.eq(AdTheatreTvStat::getTheatreTime, theatreTime)
				.eq(AdTheatreTvStat::getShowDate, adTheatre.getShowDate());
		baseMapper.delete(lambdaQueryWrapper);
	}

	@Transactional(rollbackFor = Exception.class)
	@CacheEvict(value = "TheatreAnalysis", allEntries=true)
	@Override
	public void saveStatByTheatreTv(AdTheatre adTheatre) {
		Integer theatreTime = ProjectUtil.getTypeByLocalTime(adTheatre.getShowTime());
		List<LocalTime> localTimes = ProjectUtil.getLocalTimeByType(theatreTime);

		LambdaQueryWrapper<AdTheatre> queryWrapper = new LambdaQueryWrapper<AdTheatre>().eq(AdTheatre::getName, adTheatre.getName())
				.eq(AdTheatre::getMediaName, adTheatre.getMediaName())
				.eq(AdTheatre::getShowDate, adTheatre.getShowDate());


		if (!localTimes.get(0).format(DateTimeFormatter.ISO_TIME).equals(ProjectUtil.MORNING_STR)) {
			queryWrapper.ge(AdTheatre::getShowTime, localTimes.get(0))
					.lt(AdTheatre::getShowTime, localTimes.get(1));
		} else {
			queryWrapper.apply("( (show_time >={0} ) or (show_time < {1}))", localTimes.get(0), localTimes.get(1));
		}


		List<AdTheatre> adTheatres = adTheatreMapper.selectList(queryWrapper);

		if (CollectionUtils.isEmpty(adTheatres)) {
			log.info("AdTheatre数据为空，删掉, {}, {}", queryWrapper.getCustomSqlSegment(), queryWrapper.getParamNameValuePairs());
			// 没有就要删掉
			this.delete(adTheatre, theatreTime);
		} else {
			AdTheatreTvStat adTheatreTvStat = new AdTheatreTvStat();
			adTheatre.setId(null);
			BeanUtils.copyProperties(adTheatre, adTheatreTvStat);

			adTheatreTvStat.setTheatreTime(ProjectUtil.getTypeByLocalTime(adTheatre.getShowTime()));
			adTheatreTvStat.setSeason(ProjectUtil.getSeasonByLocalDate(adTheatre.getShowDate()));
			AdMedia adMedia = adMediaService.getByName(adTheatre.getMediaName());
			adTheatreTvStat.setChannelSystem(adMedia.getRank());


			float ratio29 = 0;
			int dk29 = 0;
			float ratio51 = 0;
			int dk51 = 0;
			int total = 0;
			int total_d = 0;

//			Set<Integer> tvNumSet = new HashSet<>();

			for(AdTheatre tmp: adTheatres) {
				AdTheatreTv adTheatreTv = adTheatreTvService.getByTheatreId(tmp.getId());
				if (adTheatreTv == null) {
					continue;
				}

//				if (!tvNumSet.contains(tmp.getMediaNum())) {
//					// 一个电视剧对应多个广告
//					tvNumSet.add(tmp.getMediaNum());
					total ++;
//				}
				if (adTheatreTv.getRating29() != null && adTheatreTv.getRating29() != 0) {
					total_d ++;
				}

				ratio29 += (adTheatreTv.getRating29() == null ? 0f : adTheatreTv.getRating29());
				dk29 += (adTheatreTv.getDk29() == null ? 0 : adTheatreTv.getDk29());
				ratio51 += (adTheatreTv.getRating51() == null ? 0f : adTheatreTv.getRating51());
				dk51 += (adTheatreTv.getDk51() == null ? 0 : adTheatreTv.getDk51());
			}

			adTheatreTvStat.setDk29(dk29);
			adTheatreTvStat.setRating29(ratio29);
			adTheatreTvStat.setDk51(dk51);
			adTheatreTvStat.setRating51(ratio51);
			adTheatreTvStat.setTvNum(total);
			adTheatreTvStat.setTvNumD(total_d);

			saveOrUpd(adTheatreTvStat);

		}
	}

	@Cacheable(value = "TheatreAnalysis",keyGenerator = "cacheKeyGenerator")
	@Override
	public List<AdTheatreTopVO> getTvTop(Integer theatreTime, String startDate, String endDate) {
		List<AdTheatreTopVO> list = baseMapper.tvTop(theatreTime, startDate, endDate);
		list.stream().forEach(i -> {
			i.init();
			int count = adTheatreService.countTvNum(i.getName(), i.getMediaName(), startDate, endDate);
			i.setMediaNum(count);
		});
		return list;
	}

	@Cacheable(value = "TheatreAnalysis",keyGenerator = "cacheKeyGenerator")
	@Override
	public List<AdTheatreTopVO> getMediaTop(String name, Integer theatreTime, String startDate, String endDate, Integer limitSize) {
		limitSize = (limitSize == null ? 30 : limitSize);
		List<AdTheatreTopVO> list = baseMapper.mediaTop(name, theatreTime, startDate, endDate, limitSize);
		list.stream().forEach(i -> i.init());
		list.stream().forEach(i -> {
			MediaRankEnum mediaRankEnum = EnumHelperUtil.getOneByCode(MediaRankEnum.class, i.getMediaRank());
			i.setMediaRank(mediaRankEnum.getName());
			int count = adTheatreService.countTvNum(null, i.getMediaName(), startDate, endDate);
			i.setMediaNum(count);
		});
		return list;
	}

	@Override
	public AdTvRatioVO getTvRatioVO(AdTheatreAnalysisDetailQueryForm queryForm) {
		AdTvRatioVO adTvRatioVO = new AdTvRatioVO();
		List<AdTvRatioTrendVO> trendVOS = baseMapper.tvRatioTrend(queryForm);
		trendVOS.stream().forEach(i -> i.init());
		trendVOS.sort(new Comparator<AdTvRatioTrendVO>() {
			@Override
			public int compare(AdTvRatioTrendVO o1, AdTvRatioTrendVO o2) {
				return o1.getShowDate().compareTo(o2.getShowDate());
			}
		});
		adTvRatioVO.setTrendVOS(trendVOS);

		AdTheatreTv adTheatreTv = baseMapper.tvRatioPeople(AdTvQueryDTO.from(queryForm));
		List<AdTvRatioPeopleVO> peopleVOS = AdTvRatioPeopleVO.from(adTheatreTv);
		adTvRatioVO.setPeopleVOS(peopleVOS);

		return adTvRatioVO;
	}

	@Override
	public AdTvZoneVO getTvZoneVO(AdTheatreAnalysisDetailQueryForm queryForm) {
		AdTheatreTv adTheatreTv = baseMapper.tvRatioZone(AdTvQueryDTO.from(queryForm));
		List<AdTvZonePeopleVO> tvZone = AdTvZonePeopleVO.from(adTheatreTv);

		// 去掉电视剧
		queryForm.setName(null);
		AdTheatreTv adTheatreMedia = baseMapper.tvRatioZone(AdTvQueryDTO.from(queryForm));
		List<AdTvZonePeopleVO> mediaZone = AdTvZonePeopleVO.from(adTheatreMedia);

		AdTvZoneVO adTvZoneVO = new AdTvZoneVO();
		adTvZoneVO.setTvNum(tvZone);
		adTvZoneVO.setMediaNum(mediaZone);

		return adTvZoneVO;
	}

	@Override
	public List<AdTheatreProductDistVO> getProductVO(AdTheatreAnalysisDetailQueryForm queryForm) {
		return baseMapper.productDist(AdTvQueryDTO.from(queryForm));
	}

	@Override
	public List<AdThemeDistVO> getThemeDistVO(AdTheatreAnalysisDetailQueryForm queryForm) {
		AdTvQueryDTO queryDTO = AdTvQueryDTO.from(queryForm);
		List<AdThemeDistVO> distVOS = baseMapper.themeDist(queryForm);

		distVOS.sort(new Comparator<AdThemeDistVO>() {
			@Override
			public int compare(AdThemeDistVO o1, AdThemeDistVO o2) {
				if (o1.getRatio() > o2.getRatio()) {
					return -1;
				}
				if (o1.getRatio() < o2.getRatio()) {
					return 1;
				}
				return 0;
			}
		});

		distVOS.parallelStream().forEach(i -> {
			int total = adTheatreService.countThemeNum(i.getFirstTheme(), queryForm.getMediaName(), queryDTO.getTimeQuery(), queryForm.getStartDate(), queryForm.getEndDate());
			i.setTotal(total);
			i.setRatio(Util.newScale(i.getRatio(), 3));
		});

		return distVOS;
	}

	@CacheEvict(value = "TheatreAnalysis", allEntries=true)
	@Override
	public void batchUpdateByName(String newName, String oldName, String firstTheme, String oldFirstTheme, String secondTheme) {
		LambdaQueryWrapper<AdTheatreTvStat> statQueryWrapper = new LambdaQueryWrapper<AdTheatreTvStat>();
		statQueryWrapper.eq(AdTheatreTvStat::getName, oldName);

		AdTheatreTvStat adTheatreTvStat = new AdTheatreTvStat();
		if (!newName.equals(oldName)) {
			adTheatreTvStat.setName(newName);
		}
		if (!firstTheme.equals(oldFirstTheme)) {
			adTheatreTvStat.setFirstTheme(firstTheme);
			adTheatreTvStat.setSecondTheme(secondTheme);
		}

		baseMapper.update(adTheatreTvStat, statQueryWrapper);
	}
}
