package com.yeejoin.precontrol.common.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
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.google.common.collect.Lists;
import com.yeejoin.precontrol.common.constant.Constant;
import com.yeejoin.precontrol.common.dto.CommonPageableDto;
import com.yeejoin.precontrol.common.dto.TaskPersonDto;
import com.yeejoin.precontrol.common.dto.TaskSalaryConfirmDto;
import com.yeejoin.precontrol.common.entity.*;
import com.yeejoin.precontrol.common.enums.*;
import com.yeejoin.precontrol.common.exception.BaseException;
import com.yeejoin.precontrol.common.mapper.PersonMapper;
import com.yeejoin.precontrol.common.mapper.TaskSalaryConfirmMapper;
import com.yeejoin.precontrol.common.param.PersonParam;
import com.yeejoin.precontrol.common.publish.MessageEventPublish;
import com.yeejoin.precontrol.common.service.*;
import com.yeejoin.precontrol.common.utils.DateUtils;
import com.yeejoin.precontrol.common.utils.EasyPoiUtil;
import com.yeejoin.precontrol.common.utils.PersonUtils;
import com.yeejoin.precontrol.common.utils.StringUtil;
import com.yeejoin.precontrol.common.vo.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 薪资确认任务表 服务实现类
 *
 * @author duanwei
 * @date 2020-12-08
 */
@Service
public class TaskSalaryConfirmServiceImpl extends ServiceImpl<TaskSalaryConfirmMapper, TaskSalaryConfirm>
		implements ITaskSalaryConfirmService {

	@Autowired
	TaskSalaryConfirmMapper taskSalaryConfirmMapper;

	@Autowired
	ITaskPersonService iTaskPersonService;

	@Autowired
	ITaskSalaryConfirmAttachmentService iTaskSalaryConfirmAttachmentService;

	@Autowired
	IProjectService iProjectService;

	@Autowired
	ITaskService iTaskService;

	@Autowired
	ITaskPersonFeedbackService iTaskPersonFeedbackService;

	@Autowired
	IPersonService iPersonService;

	@Autowired
	MessageEventPublish messageEventPublish;

	@Autowired
	PersonMapper personMapper;
	
	@Autowired
	IStatisticsService statisticsService;

	private String moneyPattern = "^(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){0,2})?$";
	
	@Autowired
	SmallProService smallProService;
	
	@Autowired
	PersonUtils personUtils;

	@Override
	public IPage<TaskSalaryConfirmVo> pageInfo(CommonPageableDto pageable, TaskSalaryConfirmDto taskSalaryConfirmDto) {
		if (pageable.getIsAll() == 1) {
			pageable.setPageNumber(0);
			pageable.setPageSize(Integer.MAX_VALUE);
		}
		Page<TaskSalaryConfirmVo> page = new Page<>(pageable.getPageNumber(), pageable.getPageSize());
		IPage<TaskSalaryConfirmVo> iPage = taskSalaryConfirmMapper.pageInfo(page, taskSalaryConfirmDto);
		return iPage;
	}

	@Override
	public TaskSalaryConfirmVo queryById(Long id) {
		TaskSalaryConfirmVo taskSalaryConfirmVo = new TaskSalaryConfirmVo();
		TaskSalaryConfirm taskSalaryConfirm = this.getById(id);
		BeanUtils.copyProperties(taskSalaryConfirm, taskSalaryConfirmVo);
		List<TaskPerson> taskPersonList = iTaskPersonService
				.list(new LambdaQueryWrapper<TaskPerson>().eq(TaskPerson::getTaskId, taskSalaryConfirmVo.getTaskId()));
		taskSalaryConfirmVo.setTaskPersonList(taskPersonList);
		List<TaskSalaryConfirmAttachment> attachment = iTaskSalaryConfirmAttachmentService
				.list(new LambdaQueryWrapper<TaskSalaryConfirmAttachment>().eq(TaskSalaryConfirmAttachment::getTaskId,
						taskSalaryConfirmVo.getTaskId()));
		taskSalaryConfirmVo.setAttachment(attachment);
		return taskSalaryConfirmVo;
	}

	@Override
	@Transactional
	public Task save(TaskSalaryConfirmDto taskSalaryConfirmDto, Person person) throws ParseException {
		CommonPageableDto pageable = new CommonPageableDto();
		pageable.setIsAll(1);
		PersonParam personParam = new PersonParam();
		personParam.setCompanyId(taskSalaryConfirmDto.getCompanyId());
		personParam.setProjectId(taskSalaryConfirmDto.getProjectId());
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		Date start = sdf.parse(sdf.format(taskSalaryConfirmDto.getPayStartTime()));
		Date end = sdf.parse(sdf.format(taskSalaryConfirmDto.getPayEndTime()));
		IPage<PersonVo> page = iPersonService.period(pageable, personParam, start, end);
		BigDecimal total = new BigDecimal(0);
		Pattern pattern = Pattern.compile(moneyPattern);
		if (StringUtil.isNotEmpty(page.getRecords())
				&& StringUtil.isNotEmpty(taskSalaryConfirmDto.getTaskPersonList())) {
			for (PersonVo item : page.getRecords()) {
				boolean flag = false;
				for (PersonSalaryExcelVo excelVo : taskSalaryConfirmDto.getTaskPersonList()) {
					if (!pattern.matcher(excelVo.getSalary().toString()).matches()) {
						throw new BaseException("无法发起任务，人员薪资不正确：" + item.getName() + "，薪资：" + excelVo.getSalary());
					}
					if (item.getId().equals(excelVo.getId())) {
						item.setSalary(excelVo.getSalary());
						total = total.add(excelVo.getSalary());
						flag = true;
					}
				}
				if (!flag) {
					throw new BaseException("无法发起任务，存在人员未输入薪资：" + item.getName());
				}
			}
		} else {
			throw new BaseException("无法发起任务，薪资人员不存在或未录入人员薪资");
		}
		if (total.compareTo(taskSalaryConfirmDto.getCurrentPaySalary()) != 0) {
			throw new BaseException(
					"无法发起任务，所有薪资人员金额总和: " + total + " 不等于当前发放金额: " + taskSalaryConfirmDto.getCurrentPaySalary());
		}
		Task task = new Task();
		task.setType(TaskTypeEnum.SALARY_CONFIRM.getValue());
		task.setCreatePersonId(person.getId());
		Long projectId = taskSalaryConfirmDto.getProjectId();
		task.setProjectId(projectId);
		Project project = iProjectService.getById(projectId);
		if (project != null) {
			task.setProjectName(project.getName());
		}
		task.setStatus(Long.valueOf(TaskStatusEnum.UNCOMPLETED.getType()));
		task.setTaskNo(DateUtils.getTimeStamp());
		task.setOrgCode(taskSalaryConfirmDto.getOrgCode());
		iTaskService.save(task);

		TaskSalaryConfirm taskSalaryConfirm = new TaskSalaryConfirm();
		BeanUtils.copyProperties(taskSalaryConfirmDto, taskSalaryConfirm);
		taskSalaryConfirm.setCurrentPaySalary(taskSalaryConfirmDto.getCurrentPaySalary());
		taskSalaryConfirm.setContractTotalSalary(taskSalaryConfirmDto.getContractTotalSalary());
		taskSalaryConfirm.setTaskId(task.getId());
		this.save(taskSalaryConfirm);

		List<TaskSalaryConfirmAttachment> attachment = taskSalaryConfirmDto.getAttachment();
		if (!StringUtil.isEmpty(attachment)) {
			attachment.forEach(item -> {
				item.setTaskId(taskSalaryConfirm.getTaskId());
			});
			iTaskSalaryConfirmAttachmentService.saveBatch(attachment);
		}

		List<TaskPerson> list = new ArrayList<>();
		page.getRecords().forEach(item -> {
			TaskPerson taskperson = new TaskPerson();
			taskperson.setId(null);
			taskperson.setSalary(item.getSalary());
			taskperson.setReadStatus(StatusEnum.UNFINISHED.getValue());
			taskperson.setPersonName(item.getName());
			taskperson.setPersonId(item.getId());
			taskperson.setCompanyId(taskSalaryConfirmDto.getCompanyId());
			taskperson.setTaskId(task.getId());
			taskperson.setType(TaskPersonTypeEnum.SALARYCONFIRM.getValue());
			taskperson.setClockDays(item.getClockDays());
			taskperson.setFirstRead(false);
			list.add(taskperson);
		});
		iTaskPersonService.saveBatch(list);

		// 消息通知--薪资发放消息
		List<Long> personIdList = Lists.transform(list, TaskPerson::getPersonId);
		salaryTaskNotice(task.getId(), personIdList);
		
		//更新推送
		statisticsService.initWs(taskSalaryConfirmDto.getOrgCode(), Constant.WS_COMPOSITESUM);
		
		// 微信服务通知
		List<Person> persons = iPersonService.list(new LambdaQueryWrapper<Person>()
				.in(Person::getId, personIdList).isNotNull(Person::getOpenId));
		List<String> openIds = persons.stream().map(Person::getOpenId).collect(Collectors.toList());
		List<String> messages = new ArrayList<>();
		messages.add(smallProService.getName());
		messages.add(TaskTypeEnum.getLabelByValue(task.getType()));
		messages.add("您有待办理的薪资确认管控任务流程");
		messages.add(DateUtils.getDateNowString());
		smallProService.sendWeChatUpcomingMessage(openIds, WeChatMsgTemplateTypeEum.TODO.getValue(),
				messages);
		return task;
	}

	/**
	 * 发送薪资发放消息
	 *
	 * @param taskId 任务id
	 * @param personIdList 人员id
	 */
	private void salaryTaskNotice(Long taskId, List<Long> personIdList) {
		List<TaskPerson> taskPersonList =
				iTaskPersonService.list(new LambdaQueryWrapper<TaskPerson>().eq(TaskPerson::getTaskId, taskId).in(TaskPerson::getPersonId, personIdList));

		if (!CollectionUtils.isEmpty(taskPersonList)) {
			JSONArray array = new JSONArray();
			taskPersonList.forEach(i->{
				JSONObject jsonObject = new JSONObject();
				jsonObject.put(MessageAttributeEnum.CONTENT.getName(), MessageSubTypeEnum.SALARY_CONFIRM_MSG.getTip());
				jsonObject.put(MessageAttributeEnum.MSG_TYPE.getName(), MessageTypeEnum.SALARY_CONFIRM.getType());
				jsonObject.put(MessageAttributeEnum.PERSON_ID.getName(), i.getPersonId());
				jsonObject.put(MessageAttributeEnum.MSG_SUB_TYPE.getName(), MessageSubTypeEnum.SALARY_CONFIRM_MSG.getType());
			    jsonObject.put(MessageAttributeEnum.TASK_ID.getName(), i.getTaskId());
				array.add(jsonObject);
				
			});
			JSONObject msg = new JSONObject();
			msg.put("param", array);
			messageEventPublish.publish(msg);
		}
	}

	@Override
	public boolean confirm(TaskPersonDto taskPersonDto) {
		TaskPerson taskPerson = new TaskPerson();
		BeanUtils.copyProperties(taskPersonDto, taskPerson);
		taskPerson.setReadStatus(1L);
		taskPerson.setReadTime(new Date());
		iTaskPersonService.updateById(taskPerson);

		TaskPerson newTaskPerson = iTaskPersonService.getById(taskPersonDto.getId());
		int unConfirmCount = iTaskPersonService
				.count(new LambdaQueryWrapper<TaskPerson>().eq(TaskPerson::getTaskId, newTaskPerson.getTaskId())
						.notIn(TaskPerson::getReadStatus, java.util.Arrays.asList(StatusEnum.SUCCESS.getValue())));
		if (unConfirmCount == 0) {
			Task task = new Task();
			task.setId(newTaskPerson.getTaskId());
			task.setStatus(TaskStatusEnum.COMPLETED.getLongType());
			iTaskService.updateById(task);
			
			// 微信服务通知
			Set<Long> set = personUtils.getWXTaskBasicPerson(task.getId());
			List<Person> persons = iPersonService.list(new LambdaQueryWrapper<Person>()
					.in(Person::getId, set).isNotNull(Person::getOpenId));
			List<String> openIds = persons.stream().map(Person::getOpenId).collect(Collectors.toList());
			List<String> messages = new ArrayList<>();
			messages.add(smallProService.getName());
			messages.add(TaskTypeEnum.getLabelByValue(task.getType()));
			messages.add(DateUtils.getDateNowString());
			messages.add(TaskTypeEnum.getLabelByValue(task.getType()) + "任务完成消息通知");
			smallProService.sendWeChatUpcomingMessage(openIds, WeChatMsgTemplateTypeEum.SERVICE_COMPLETE.getValue(),
					messages);
		}

		// 消息通知--薪资确认
		sendMsg(newTaskPerson);

		return true;
	}

	private void sendMsg(TaskPerson taskPerson) {
		// 消息接收人列表
		List<Long> personIdList = Lists.newArrayList();

		// 1、消息通知--薪资确认--给任务创建人发送消息
		Task task = iTaskService.getById(taskPerson.getTaskId());
		if (task != null) {
			personIdList.add(task.getCreatePersonId());
		}
		// 2、消息通知--薪资确认--给分包商主管发送消息
		List<Person> fbsDirectorList = personMapper.getPersonByTaskId(task.getId());
		List<Long> idList = Lists.transform(fbsDirectorList, Person::getId);
		personIdList.addAll(idList);
		// 发送消息
		salaryTaskNotice(task.getId(), personIdList);
	}

	@Override
	public IPage<TaskSalaryConfirmOneVo> todo(CommonPageableDto pageable,
			TaskSalaryConfirmOneVo taskSalaryConfirmOneVo) {
		if (pageable.getIsAll() == 1) {
			pageable.setPageNumber(0);
			pageable.setPageSize(Integer.MAX_VALUE);
		}
		Page<TaskSalaryConfirmOneVo> page = new Page<>(pageable.getPageNumber(), pageable.getPageSize());
		IPage<TaskSalaryConfirmOneVo> iPage = taskSalaryConfirmMapper.pageTodo(page, taskSalaryConfirmOneVo);
		return iPage;
	}

	@Override
	public TaskSalaryConfirmOneVo todoDetail(long taskPersonId,long personId) {
		TaskSalaryConfirmOneVo taskSalaryConfirmOneVo = taskSalaryConfirmMapper.pageTodoDetail(taskPersonId,personId);
		List<TaskPersonFeedback> feedbackList = iTaskPersonFeedbackService.list(
				new LambdaQueryWrapper<TaskPersonFeedback>().eq(TaskPersonFeedback::getTaskPersonId, taskPersonId));
		taskSalaryConfirmOneVo.setFeedbackList(feedbackList);
		List<TaskSalaryConfirmAttachment> attachment = iTaskSalaryConfirmAttachmentService
				.list(new LambdaQueryWrapper<TaskSalaryConfirmAttachment>().eq(TaskSalaryConfirmAttachment::getTaskId,
						taskSalaryConfirmOneVo.getTaskId()));
		taskSalaryConfirmOneVo.setAttachment(attachment);
		Person person = iPersonService.getById(taskSalaryConfirmOneVo.getPersonId());
		taskSalaryConfirmOneVo.setPerson(person);
		return taskSalaryConfirmOneVo;
	}

	@Override
	public List<PersonSalaryExcelVo> personImport(MultipartFile file) throws Exception {
		List<PersonSalaryExcelVo> personList = EasyPoiUtil.importExcel(file, 1, 1, PersonSalaryExcelVo.class);
		List<PersonSalaryExcelVo> newPersonList = new ArrayList<>();
		personList.forEach(item -> {
			if (item.getIdnumber() != null) {
				Pattern pattern = Pattern.compile(moneyPattern);
				if (!pattern.matcher(item.getSalary().toString()).matches()) {
					throw new BaseException("无法导入，人员薪资不正确：" + item.getName() + ",薪资：" + item.getSalary());
				}
				Person person = iPersonService
						.getOne(new LambdaQueryWrapper<Person>().eq(Person::getIdNumber, item.getIdnumber()));
				if (person != null) {
					item.setId(person.getId());
					newPersonList.add(item);
				}
			}
		});
		return newPersonList;
	}

	@Override
	public IPage<PersonVo> persons(Long id, CommonPageableDto pageable, PersonParam personParam) {
		if (pageable.getIsAll() == 1) {
			pageable.setPageNumber(0);
			pageable.setPageSize(Integer.MAX_VALUE);
		}
		TaskSalaryConfirm taskSalaryConfirm = this.getById(id);
		Page<PersonVo> page = new Page<>(pageable.getPageNumber(), pageable.getPageSize());
		IPage<PersonVo> iPageData = taskSalaryConfirmMapper.persons(page, taskSalaryConfirm.getTaskId(), personParam);
		iPageData.getRecords().forEach(personVo -> {
			List<TaskPersonFeedback> list = iTaskPersonFeedbackService.list(new LambdaQueryWrapper<TaskPersonFeedback>()
					.eq(TaskPersonFeedback::getTaskPersonId, personVo.getTaskPersonId()));
			List<TaskPersonFeedbackVo> feedBackList = new ArrayList<>();
			list.forEach(item -> {
				TaskPersonFeedbackVo taskPersonFeedbackVo = new TaskPersonFeedbackVo();
				BeanUtils.copyProperties(item, taskPersonFeedbackVo);
				taskPersonFeedbackVo.setTypeName(TaskFeedBackEnum.getNameByType(taskPersonFeedbackVo.getType()));
				feedBackList.add(taskPersonFeedbackVo);
			});
			personVo.setFeedbackList(feedBackList);
		});
		return iPageData;
	}

	@Override
	public boolean resend(TaskSalaryConfirmOneVo salaryConfirmOneVo) {
		TaskPerson taskPerson = new TaskPerson();
		taskPerson.setId(salaryConfirmOneVo.getTaskPersonId());
		taskPerson.setReadStatus(StatusEnum.UNFINISHED.getValue());
		taskPerson.setSalary(salaryConfirmOneVo.getSalary());
		taskPerson.setCreateDate(new Date());
		taskPerson.setFirstRead(false);
		iTaskPersonService.updateById(taskPerson);
		return true;
	}

	@Override
	public TaskSalaryConfirmVo statistics(long id) {
		TaskSalaryConfirm taskSalaryConfirm = this.getById(id);
		Long taskId = taskSalaryConfirm.getTaskId();
		int totalPersonCount = iTaskPersonService
				.count(new LambdaQueryWrapper<TaskPerson>().eq(TaskPerson::getTaskId, taskId));
		int confirmPersonCount = iTaskPersonService.count(new LambdaQueryWrapper<TaskPerson>()
				.eq(TaskPerson::getTaskId, taskId).eq(TaskPerson::getReadStatus, StatusEnum.SUCCESS.getValue()));
		int unconfirmPersonCount = iTaskPersonService.count(new LambdaQueryWrapper<TaskPerson>()
				.eq(TaskPerson::getTaskId, taskId).eq(TaskPerson::getReadStatus, StatusEnum.UNFINISHED.getValue()));
		;
		int feedBackPersonCount = iTaskPersonService.count(new LambdaQueryWrapper<TaskPerson>()
				.eq(TaskPerson::getTaskId, taskId).eq(TaskPerson::getReadStatus, StatusEnum.FEEDBACK.getValue()));
		;
		TaskSalaryConfirmVo taskSalaryConfirmVo = new TaskSalaryConfirmVo();
		taskSalaryConfirmVo.setTotalPersonCount(totalPersonCount);
		taskSalaryConfirmVo.setConfirmPersonCount(confirmPersonCount);
		taskSalaryConfirmVo.setUnconfirmPersonCount(unconfirmPersonCount);
		taskSalaryConfirmVo.setFeedBackPersonCount(feedBackPersonCount);
		return taskSalaryConfirmVo;
	}

	@Override
	public Integer paidRatio(Long projectId, Long companyId) {
		return taskSalaryConfirmMapper.paidRatio(projectId, companyId);
	}

	@Override
	public List<PersonVo> warnPersons(int day) {
		return taskSalaryConfirmMapper.warnPersons(day);
	}

	@Override
	public boolean hasFirstRead(long personId) {
		int count = taskSalaryConfirmMapper.firstReadCount(personId);
		return count > 0;
	}

	@Override
	public boolean doFirstRead(long taskPersonId) {
		TaskPerson taskPerson = iTaskPersonService.getById(taskPersonId);
		taskPerson.setFirstRead(true);
		iTaskPersonService.updateById(taskPerson);
		return true;
	}
}
