package com.persagy.calendar.handle;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.persagy.calendar.constant.WorkCalendarConstant;
import com.persagy.calendar.pojo.dto.WorkCalendar;
import com.persagy.calendar.pojo.dto.WorkCalendarDict;
import com.persagy.calendar.pojo.dto.WorkCalendarLabel;
import com.persagy.calendar.pojo.dto.WorkCalendarLabel.Builder;
import com.persagy.calendar.pojo.dto.WorkCalendarObject;
import com.persagy.calendar.pojo.vo.WorkCalendarLabelQueryVO;
import com.persagy.calendar.pojo.vo.WorkCalendarLabelUpdateVO;
import com.persagy.calendar.pojo.vo.WorkCalendarMoreDateCreateVO;
import com.persagy.calendar.pojo.vo.WorkLabelBatchUpdateVO;
import com.persagy.calendar.service.IWorkCalendarDictService;
import com.persagy.calendar.service.IWorkCalendarLabelService;
import com.persagy.calendar.service.IWorkCalendarObjectService;
import com.persagy.calendar.service.IWorkCalendarService;
import com.persagy.common.enums.ResponseCode;
import com.persagy.common.exception.BusinessException;
import com.persagy.common.utils.DateUtil;
import com.persagy.common.utils.IdGenerator;
import com.persagy.common.utils.ResponseResult;
import com.persagy.common.utils.ResponseUtil;
import com.persagy.common.utils.StringUtil;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.util.BooleanUtil;

/**
 * @version 
 * @description 
 * @company persagy
 * @author zhangqiankun
 * @since 2021年3月3日:	下午6:14:49
 */
@Component
public class WorkCalendarLabelHandler {
	
	public static final String LABEL_COLUMN = "GROUP_CODE, PROJECT_ID, CALENDAR_ID, DICT_TYPE, DICT_CODE, DIY_LABEL, DIY_VALUE, LABEL_DATE, CONVERT(SUBSTR(LABEL_DATE, 5, 2), SIGNED) AS labelMonth, CREATE_TIME, UPDATE_TIME, UPDATE_USER";
	
    @Autowired
    private IWorkCalendarService workCalendarService;
    
	@Autowired
	private IWorkCalendarLabelService workCalendarLabelService;
	
	@Autowired
	private IWorkCalendarDictService workCalendarDictService;
	
	@Autowired
	private WorkCalendarDateHandler workCalendarDateHandler;
	
	@Autowired
	private IWorkCalendarObjectService workCalendarObjectService;

	/**
	 * 日期标签属性列表查询
	 * 
	 * @param queryVO
	 * @return
	 */
	public List<WorkCalendarLabel> queryCalendarLabelList(WorkCalendarLabelQueryVO queryVO) {
		// 获取字典类型的级别
		int dictTypeLevel = this.workCalendarDictService.getDictTypeLevel(queryVO.getGroupCode(), queryVO.getDictType());
		
		// 如果条件为对象ID，且属于工作历级别的类型，反查出工作历ID，再进行查询
		if (StringUtil.isNotBlank(queryVO.getObjId()) && dictTypeLevel == 2) {
			//查询出此对象对应的工作历ID和项目ID
			QueryWrapper<WorkCalendarObject> objectWrapper = new WorkCalendarObject.Builder().createQueryWrapper()
					.groupCodeEq(queryVO.getGroupCode()).objectIdEq(queryVO.getObjId())
					.projectIdEq(queryVO.getProjectId()).builderQueryWrapper();
			WorkCalendarObject calendarObject = this.workCalendarObjectService.getOne(objectWrapper);
			if (calendarObject == null) {
				return null;
			}
			queryVO.setCalendarId(calendarObject.getCalendarId());
		}
		
		QueryWrapper<WorkCalendarLabel> queryWrapper = new WorkCalendarLabel.Builder().createQueryWrapper().groupCodeEq(queryVO.getGroupCode())
				.projectIdEq(queryVO.getProjectId(), dictTypeLevel).calendarIdEq(queryVO.getCalendarId(), dictTypeLevel)
				.dictTypeEq(queryVO.getDictType()).dictCodeEq(queryVO.getDictCode()).diyLabelEq(queryVO.getDiyLabel())
				.labelDateGe(queryVO.getLabelDateStart()).labelDateLe(queryVO.getLabelDateEnd()).builderQueryWrapper();
		return this.workCalendarLabelService.list(queryWrapper.select(LABEL_COLUMN).orderByAsc(Lists.newArrayList("LABEL_DATE", "ID")));
	}
	
	/**
	 * 查询标签的有效期，仅允许查询某一项目、某一标签对应的有效期
	 * 
	 * @param queryVO
	 * @return
	 */
	public List<WorkCalendarLabel> queryCalendarLabelValid(WorkCalendarLabelQueryVO queryVO) {
		// 获取字典类型的级别
		int dictTypeLevel = this.workCalendarDictService.getDictTypeLevel(queryVO.getGroupCode(), queryVO.getDictType());
		
		if (StringUtil.isNotBlank(queryVO.getObjId()) && dictTypeLevel == 2) {
			//查询出此对象对应的工作历ID和项目ID
			QueryWrapper<WorkCalendarObject> objectWrapper = new WorkCalendarObject.Builder().createQueryWrapper()
					.groupCodeEq(queryVO.getGroupCode()).objectIdEq(queryVO.getObjId())
					.projectIdEq(queryVO.getProjectId()).builderQueryWrapper();
			WorkCalendarObject calendarObject = this.workCalendarObjectService.getOne(objectWrapper);
			if (calendarObject == null) {
				return null;
			}
			queryVO.setCalendarId(calendarObject.getCalendarId());
		}
		
		QueryWrapper<WorkCalendarLabel> queryWrapper = new WorkCalendarLabel.Builder().createQueryWrapper().groupCodeEq(queryVO.getGroupCode())
				.projectIdEq(queryVO.getProjectId(), dictTypeLevel).calendarIdEq(queryVO.getCalendarId(), dictTypeLevel)
				.dictTypeEq(queryVO.getDictType()).dictCodeEq(queryVO.getDictCode()).diyLabelEq(queryVO.getDiyLabel())
				.labelDateGe(queryVO.getLabelDateStart()).labelDateLe(queryVO.getLabelDateEnd()).builderQueryWrapper();
		queryWrapper.select("GROUP_CODE, PROJECT_ID, CALENDAR_ID, DICT_TYPE, DICT_CODE, LABEL_DATE, DIY_LABEL").orderByAsc("DICT_CODE");
		List<WorkCalendarLabel> labels = this.workCalendarLabelService.list(queryWrapper);
		return this.convert2Response(labels);
	}
	
	/**
	 * 结果封装
	 * 
	 * @param labels 此集合内容，必须是同一项目、同一标签类型下的数据
	 * @return
	 */
	private List<WorkCalendarLabel> convert2Response(List<WorkCalendarLabel> labels) {
		if (CollectionUtil.isEmpty(labels)) {
			return null;
		}
		
		// 生成响应的数据结构
		Map<String, WorkCalendarLabel> dictCode2Label = new HashMap<String, WorkCalendarLabel>();
		Map<String, List<String>> dictCode2Date = new HashMap<String, List<String>>();
		for (WorkCalendarLabel workCalendarLabel : labels) {
			String dictCode = workCalendarLabel.getDictCode();
			
			if (!dictCode2Label.containsKey(dictCode)) {
				dictCode2Label.put(dictCode, workCalendarLabel);
			}
			
			if (dictCode2Date.containsKey(dictCode)) {
				dictCode2Date.get(dictCode).add(workCalendarLabel.getLabelDate());
			} else {
				dictCode2Date.put(dictCode, Lists.newArrayList(workCalendarLabel.getLabelDate()));
			}
		}
		
		List<WorkCalendarLabel> resultList = new ArrayList<WorkCalendarLabel>();
		
		// 解析出对应code每个时间段内的最小日期和最大日期
		Set<String> dictCodes = dictCode2Date.keySet();
		for (String dictCode : dictCodes) {
			List<String> labelDates = dictCode2Date.get(dictCode);
			WorkCalendarLabel calendarLabel = dictCode2Label.get(dictCode);
			
			if (labelDates.size() == 1) {
				this.convert2Response(calendarLabel, null, null);
				continue;
			}
			// 先排序
			Collections.sort(labelDates);
			
			int size = labelDates.size();
			String preDate = labelDates.get(0);
			String minDate = labelDates.get(0);
			
			DateTime currentDate = null;
			DateTime preDateTime = DateUtil.parse(labelDates.get(0), DateUtil.FORMAT_DATE_YYYYMMDD);
			for (int i = 1; i < size; i++) {
				// 获取当前时间和日期
				String labelDate = labelDates.get(i);
				currentDate = DateUtil.parse(labelDate, DateUtil.FORMAT_DATE_YYYYMMDD);
				
				// 当前时间往前偏移一天，判断是否相等
				currentDate.offset(DateField.DAY_OF_YEAR, -1);
				if (preDateTime.equals(currentDate)) {
				} else {
					// 两个日期不相等的情况下，证明当前日期不连续，封装响应结果
					resultList.add(this.convert2Response(calendarLabel, minDate, preDate));
					minDate = labelDate;
				}
				// 无论哪种情况下，都需要为上一日期赋最新值
				preDate = labelDate;
				preDateTime = DateUtil.parse(labelDate, DateUtil.FORMAT_DATE_YYYYMMDD);
				
				// 最后一天无论与之前的相不相等，都应该封装响应结果
				if (i == (size - 1)) {
					resultList.add(convert2Response(calendarLabel, minDate, labelDate));
				}
			}
		}
		
		return resultList;
	}
	
	
	/**
	 * 创建或更新日期标签属性
	 * 
	 * @param createVO
	 */
	@Transactional
	public void updateLabelInfo(WorkCalendarLabelUpdateVO updateVO) {
		// 获取字典类型的级别
		int dictTypeLevel = this.workCalendarDictService.getDictTypeLevel(updateVO.getGroupCode(), updateVO.getDictType());
		
		boolean result = false;
		if (BooleanUtil.isTrue(updateVO.getIsDelete())) {
			Builder builder = new WorkCalendarLabel.Builder().createQueryWrapper();
			QueryWrapper<WorkCalendarLabel> queryWrapper = builder.groupCodeEq(updateVO.getGroupCode())
					.projectIdEq(updateVO.getProjectId(), dictTypeLevel).calendarIdEq(updateVO.getCalendarId(), dictTypeLevel)
					.labelDateGe(updateVO.getLabelDateStart()).labelDateLe(updateVO.getLabelDateEnd())
					.dictTypeEq(updateVO.getDictType()).diyLabelEq(updateVO.getDiyLabel()).builderQueryWrapper();
			this.workCalendarLabelService.remove(queryWrapper);
			return;
		} else if (BooleanUtil.isTrue(updateVO.getIsUpdate())) {
			Builder builder = new WorkCalendarLabel.Builder().createQueryWrapper();
			QueryWrapper<WorkCalendarLabel> queryWrapper = builder.groupCodeEq(updateVO.getGroupCode())
					.projectIdEq(updateVO.getProjectId(), dictTypeLevel).calendarIdEq(updateVO.getCalendarId(), dictTypeLevel)
					.labelDateGe(updateVO.getLabelDateStart()).labelDateLe(updateVO.getLabelDateEnd())
					.dictTypeEq(updateVO.getDictType()).diyLabelEq(updateVO.getDiyLabel()).builderQueryWrapper();
			this.workCalendarLabelService.remove(queryWrapper);
		}
		
		WorkCalendarLabel calendarLabel = new WorkCalendarLabel();
		BeanUtils.copyProperties(updateVO, calendarLabel);
		String now = DateUtil.format(new Date(), DateUtil.FORMAT_DATE_YYYYMMDDHHMMSS);
		calendarLabel.setCreateTime(now);
		calendarLabel.setUpdateTime(now);
		calendarLabel.setUpdateUser(updateVO.getUserId());
		calendarLabel.setGroupCode(updateVO.getGroupCode());
		if (dictTypeLevel == 0) {
			// 集团级
			calendarLabel.setCalendarId("0");
			calendarLabel.setProjectId("0");
		} else if (dictTypeLevel == 1) {
			// 项目级
			calendarLabel.setCalendarId("0");
		}
		
		DateTime dateStart = DateUtil.parse(updateVO.getLabelDateStart(), DateUtil.FORMAT_DATE_YYYYMMDD);
		DateTime dateEnd = DateUtil.parse(updateVO.getLabelDateEnd(), DateUtil.FORMAT_DATE_YYYYMMDD);
		long betweenDay = DateUtil.between(dateStart, dateEnd, DateUnit.DAY) + 1;
		for (int i = 0; i < betweenDay; i++) {
			calendarLabel.setId(null);
			calendarLabel.setLabelDate(DateUtil.format(dateStart, DateUtil.FORMAT_DATE_YYYYMMDD));
			if (WorkCalendarConstant.CUSTOM_CALENDAR_DATE_YES.equals(updateVO.getDiyLabel())) {
				calendarLabel.setDictType(WorkCalendarConstant.CUSTOM_CALENDAR_DICT_TYPE);
			}
			result = this.workCalendarLabelService.save(calendarLabel);
			if (!result) {
				throw new BusinessException("日期标签属性设置失败");
			}
			
			dateStart.offset(DateField.DAY_OF_YEAR, 1);
		}
		
		// 设置成功后，将自定义的标签插入到字典表中
		if (WorkCalendarConstant.CUSTOM_CALENDAR_DATE_YES.equals(updateVO.getDiyLabel())) {
			// 当自定义标签时，存入字典表
			List<WorkCalendarDict> calendarDicts = new ArrayList<WorkCalendarDict>();
			Set<String> diyValues = updateVO.getDiyValue();
			for (String diyValue : diyValues) {
				WorkCalendarDict calendarDict = new WorkCalendarDict();
				calendarDict.setId(IdGenerator.getUUID());
				calendarDict.setCalendarId(updateVO.getCalendarId());
				calendarDict.setProjectId(updateVO.getProjectId());
				calendarDict.setGroupCode(updateVO.getGroupCode());
				calendarDict.setDictType(WorkCalendarConstant.CUSTOM_CALENDAR_DICT_TYPE);
	    		calendarDict.setDictDesc(diyValue);
	    		calendarDicts.add(calendarDict);
			}
			this.workCalendarDictService.batchCreateDictTypeByDelete(calendarDicts);
		}
		
	}

	/**
	 * 重新定义日期段内每天的属性标签
	 * 
	 * @param updateVO
	 */
	@Transactional
	public void updateCalendarDateAttribute(WorkLabelBatchUpdateVO updateVO) {
		List<WorkCalendarMoreDateCreateVO> times = updateVO.getTimes();
		if (CollectionUtil.isNotEmpty(times)) {
			// 自定义时间时，一定是属于某个工作历下的，不用判断标签所属
			for (WorkCalendarMoreDateCreateVO moreDateCreateVO : times) {
				// 判断工作历类型是否存在
				WorkCalendar calendar = this.workCalendarService.getById(moreDateCreateVO.getCalendarId(), moreDateCreateVO.getGroupCode(), moreDateCreateVO.getProjectId());
				if (calendar == null) {
					throw new BusinessException("工作历类型不存在");
				}
				moreDateCreateVO.setGroupCode(updateVO.getGroupCode());
				this.workCalendarDateHandler.batchCreateCalendarMoreDate(moreDateCreateVO, calendar);
			}
		}
		
		List<WorkCalendarLabelUpdateVO> labels = updateVO.getLabels();
		if (CollectionUtil.isNotEmpty(labels)) {
			for (WorkCalendarLabelUpdateVO labelUpdateVO : labels) {
				labelUpdateVO.setGroupCode(updateVO.getGroupCode());
				this.updateLabelInfo(labelUpdateVO);
			}
		}
	}
	
	/**
	 * 检查数据的有效性
	 * @param updateVO
	 * @return null - 有效
	 */
	public static ResponseResult checkDataValidity(WorkCalendarLabelUpdateVO updateVO) {
		if (WorkCalendarConstant.CUSTOM_CALENDAR_DATE_NO.equals(updateVO.getDiyLabel())) {
			if (StringUtil.isBlank(updateVO.getDictType())) {
				return ResponseUtil.errorResult(ResponseCode.A0400.getCode(), "字典类型不可为空");
			}
			if (BooleanUtil.isFalse(updateVO.getIsDelete())) {
				if (StringUtil.isBlank(updateVO.getDictCode())) {
					return ResponseUtil.errorResult(ResponseCode.A0400.getCode(), "字典编码不可为空");
				}
				if (CollectionUtil.isNotEmpty(updateVO.getDiyValue())) {
					return ResponseUtil.errorResult(ResponseCode.A0400.getCode(), "非自定义情况下，diyValue必须为空");
				}
			}
		} else {
			if (BooleanUtil.isFalse(updateVO.getIsDelete())) {
				if (CollectionUtil.isEmpty(updateVO.getDiyValue())) {
					return ResponseUtil.errorResult(ResponseCode.A0400.getCode(), "自定义标签值不可为空");
				}
				if (StringUtil.isNotBlank(updateVO.getDictType()) || StringUtil.isNotBlank(updateVO.getDictCode())) {
					return ResponseUtil.errorResult(ResponseCode.A0400.getCode(), "自定义情况下，字典类型和字典编码必须为空");
				}
			}
		}
		
		return null;
	}

	/**
	 * 转为响应数据
	 * 
	 * @param temp
	 * @param labelDateStart
	 * @param labelDateEnd
	 * @return
	 */
	private WorkCalendarLabel convert2Response(WorkCalendarLabel temp, String labelDateStart, String labelDateEnd) {
		WorkCalendarLabel result = new WorkCalendarLabel();
		BeanUtils.copyProperties(temp, result);
		result.setLabelDate(null);
		result.setLabelDateStart(labelDateStart == null ? temp.getLabelDateStart() : labelDateStart);
		result.setLabelDateEnd(labelDateEnd == null ? temp.getLabelDateEnd() : labelDateEnd);
		return result;
	}
	
}
