package com.prolog.cs.biz.inbound.service.impl.task;

import com.prolog.cs.biz.inbound.constant.BizConstant;
import com.prolog.cs.biz.inbound.constant.OperationConstant;
import com.prolog.cs.biz.inbound.dao.task.TaskExceptionDetailMapper;
import com.prolog.cs.biz.inbound.dao.task.TaskInstockDetailMapper;
import com.prolog.cs.biz.inbound.dao.task.TaskQcDetailMapper;
import com.prolog.cs.biz.inbound.dto.PageParam;
import com.prolog.cs.biz.inbound.dto.rcv.RcvHDTO;
import com.prolog.cs.biz.inbound.dto.rcv.RcvInstockLinesDTO;
import com.prolog.cs.biz.inbound.dto.rqc.RqcLinesDTO;
import com.prolog.cs.biz.inbound.dto.task.TaskExceptionDetailDTO;
import com.prolog.cs.biz.inbound.model.task.TaskExceptionDetail;
import com.prolog.cs.biz.inbound.model.task.TaskInstockDetail;
import com.prolog.cs.biz.inbound.model.task.TaskQcDetail;
import com.prolog.cs.biz.inbound.service.impl.BaseServiceImpl;
import com.prolog.cs.biz.inbound.service.task.TaskExceptionDetailService;
import com.prolog.cs.biz.inbound.utils.ObjectUtils;
import com.prolog.cs.biz.inbound.utils.UserUtils;
import com.prolog.cs.biz.inbound.vo.RcvQueryVO;
import com.prolog.cs.biz.inbound.vo.task.TaskQueryVO;
import com.prolog.framework.authority.core.dto.UserDTO;
import com.prolog.framework.beans.BeanUtils;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.pojo.Page;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Order;
import com.prolog.framework.core.restriction.Restriction;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.framework.dao.util.PageUtils;
import com.prolog.framework.utils.AssertUtils;
import com.prolog.framework.utils.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Validator;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class TaskExceptionDetailServiceImpl extends BaseServiceImpl<TaskExceptionDetail>
		implements TaskExceptionDetailService {

	@Resource
	private TaskExceptionDetailMapper mapper;

	@Resource
	private UserUtils userUtils;

	@Resource
	private Validator validator;

	@Resource
	private TaskQcDetailMapper taskQcMapper;

	@Resource
	private TaskInstockDetailMapper taskInstockMapper;

	private static final String SERVICE_MSG = "异常任务池业务";

	private static Logger log = LoggerFactory.getLogger(TaskExceptionDetailServiceImpl.class);

	/**
	 * 提取任务
	 */
	@Override
	public Page<TaskExceptionDetailDTO> extractTaskWithNativeSql(TaskQueryVO taskQueryVO, PageParam pageParam) {
		// 提取未执行任务的数据
		AssertUtils.notNull(taskQueryVO, SERVICE_MSG + OperationConstant.QUERY_OBJ_NULL_MSG);
		taskQueryVO.setStatus("1");
		return this.findList(taskQueryVO, pageParam);
	}

	/**
	 * 提取收货头
	 */
	@Override
	public Page<RcvHDTO> extractRcvHWithNativeSql(RcvQueryVO rcvQueryVO, PageParam pageParam) {
		// 提取未执行任务的收货头信息
		AssertUtils.notNull(rcvQueryVO, SERVICE_MSG + OperationConstant.QUERY_OBJ_NULL_MSG);
		rcvQueryVO.setEnterpriseId(userUtils.getEnterpriseId());
		this.startPage(pageParam);
		List<RcvHDTO> list = this.mapper.extractRcvHWithNativeSql(rcvQueryVO);
		return PageUtils.getPage(list);
	}

	@Override
	public Page<TaskExceptionDetailDTO> findList(TaskQueryVO queryVO, PageParam pageParam) {
		// 提取未执行任务的数据
		AssertUtils.notNull(queryVO, SERVICE_MSG + OperationConstant.QUERY_OBJ_NULL_MSG);
		queryVO.setEnterpriseId(userUtils.getEnterpriseId());
		this.startPage(pageParam);
		List<TaskExceptionDetailDTO> list = this.mapper.findList(queryVO);
		return PageUtils.getPage(list);
	}

	@Override
	public TaskExceptionDetailDTO findByPkId(String pkId) {
		// 主键id查询详情
		AssertUtils.isNotBlank(pkId, SERVICE_MSG + OperationConstant.PK_NULL_MSG);
		return this.mapper.findByPkId(pkId);
	}

	@Override
	public Object add(TaskExceptionDetailDTO taskExceptionDetailDTO) {
		// TODO vaildate
		UserDTO userDTO = userUtils.getUserDTO();
		TaskExceptionDetail taskExceptionDetail = new TaskExceptionDetail();
		BeanUtils.copyProperties(taskExceptionDetailDTO, taskExceptionDetail);
		this.vaildateModel(taskExceptionDetail, false, userDTO);
		this.buildField(taskExceptionDetail, false, userDTO);
		this.mapper.save(taskExceptionDetail);
		return "";
	}

	@Override
	public Object edit(String pkId, TaskExceptionDetailDTO taskExceptionDetailDTO) {
		// TODO
		UserDTO userDTO = userUtils.getUserDTO();
		TaskExceptionDetail taskExceptionDetail = new TaskExceptionDetail();
		BeanUtils.copyProperties(taskExceptionDetailDTO, taskExceptionDetail);
		this.vaildateModel(taskExceptionDetail, true, userDTO);
		this.buildField(taskExceptionDetail, true, userDTO);
		this.mapper.updateWithValidate(taskExceptionDetail,
				p -> ObjectUtils.isNotNull(p) && ObjectUtils.isNotNull(p.getValue()));
		return "";
	}

	@Override
	@Transactional
	public Object delIds(List<String> pkIdList) {
		// TODO
		pkIdList.stream().filter(p -> canEdit(p)).forEach(p -> this.mapper.deleteById(p, TaskExceptionDetail.class));
		return "";
	}

	@Override
	public Object deletePkId(String pkId) {
		// TODO
		if (canEdit(pkId))
			throw new BizException("can not del");
		this.mapper.deleteById(pkId, TaskExceptionDetail.class);
		return "";
	}

	@Override
	public Page<TaskExceptionDetailDTO> findList(TaskExceptionDetailDTO taskExceptionDetailDTO, PageParam pageParam) {
		Criteria criteria = Criteria.forClass(TaskExceptionDetail.class);
		Integer pageNum = null;
		Integer pageSize = null;
		Restriction r = null;
		if (pageParam == null) {
			pageNum = PageParam.DEFAULT_PAGE_NUM;
			pageSize = PageParam.DEFAULT_PAGE_SIZE;
		} else {
			pageNum = pageParam.getPageNum();
			pageSize = pageParam.getPageSize();
			TaskExceptionDetailDTO condition = taskExceptionDetailDTO;
			r = buildCondition(condition);
		}
		if (pageNum == null || pageNum == 0) {
			pageNum = PageParam.DEFAULT_PAGE_NUM;
		}
		if (pageSize == null || pageSize == 0) {
			pageSize = PageParam.DEFAULT_PAGE_SIZE;
		}
		criteria.setRestriction(r);
		Order order = Order.newInstance().desc("gmtModified");
		criteria.setOrder(order);
		PageUtils.startPage(pageNum, pageSize);
		List<TaskExceptionDetail> list = this.mapper.findByCriteria(criteria);
		List<TaskExceptionDetailDTO> collect = Optional.ofNullable(list).orElse(Collections.emptyList()).stream()
				.map(p -> {
					TaskExceptionDetailDTO target = new TaskExceptionDetailDTO();
					BeanUtils.copyProperties(p, target);
					return target;
				}).collect(Collectors.toList());
		Page<TaskExceptionDetail> page = PageUtils.getPage(list);
		Page<TaskExceptionDetailDTO> page2 = new Page<>();
		page2.setStartRow(page.getStartRow());
		page2.setEndRow(page.getEndRow());
		page2.setPageCount(page.getPageCount());
		page2.setPageNum(page.getPageNum());
		page2.setPageSize(page.getPageSize());
		page2.setTotalCount(page.getTotalCount());
		page2.setList(collect);
		return page2;
	}

	@Override
	@Transactional
	public Object batchAdd(List<TaskExceptionDetailDTO> taskExceptionDetailDTOList) {
		// TODO
		UserDTO userDTO = userUtils.getUserDTO();
		for (TaskExceptionDetailDTO taskExceptionDetailDTO : taskExceptionDetailDTOList) {
			TaskExceptionDetail taskExceptionDetail = new TaskExceptionDetail();
			BeanUtils.copyProperties(taskExceptionDetailDTO, taskExceptionDetail);
			this.vaildateModel(taskExceptionDetail, false, userDTO);
			this.buildField(taskExceptionDetail, false, userDTO);
			this.mapper.save(taskExceptionDetail);
		}
		return "";
	}

	@Override
	@Transactional
	public Object batchEdit(List<TaskExceptionDetailDTO> taskExceptionDetailDTOList) {
		// TODO
		UserDTO userDTO = userUtils.getUserDTO();
		for (TaskExceptionDetailDTO taskExceptionDetailDTO : taskExceptionDetailDTOList) {
			TaskExceptionDetail taskExceptionDetail = new TaskExceptionDetail();
			BeanUtils.copyProperties(taskExceptionDetailDTO, taskExceptionDetail);
			this.vaildateModel(taskExceptionDetail, true, userDTO);
			this.buildField(taskExceptionDetail, true, userDTO);
			this.mapper.updateWithValidate(taskExceptionDetail,
					p -> ObjectUtils.isNotNull(p) && ObjectUtils.isNotNull(p.getValue()));
		}
		return "";
	}

	@Override
	public List<TaskExceptionDetailDTO> findAll(TaskExceptionDetailDTO taskExceptionDetailDTO) {
		// TODO
		Criteria criteria = Criteria.forClass(TaskExceptionDetail.class);
		TaskExceptionDetailDTO condition = taskExceptionDetailDTO;
		Restriction r = buildCondition(condition);
		criteria.setRestriction(r);
		Order order = Order.newInstance().desc("gmtModified");
		criteria.setOrder(order);
		List<TaskExceptionDetail> list = this.mapper.findByCriteria(criteria);
		List<TaskExceptionDetailDTO> collect = Optional.ofNullable(list).orElse(Collections.emptyList()).stream()
				.map(p -> {
					TaskExceptionDetailDTO target = new TaskExceptionDetailDTO();
					BeanUtils.copyProperties(p, target);
					return target;
				}).collect(Collectors.toList());
		return collect;
	}

	private TaskExceptionDetail findById(String id) {
		return this.mapper.findById(id, TaskExceptionDetail.class);
	}

	private void vaildateModel(TaskExceptionDetail taskExceptionDetail, Boolean editFlag, UserDTO userDTO) {
		if (!editFlag) {
			// TODO add
		} else {
			// TODO
		}
	}

	private void buildField(TaskExceptionDetail taskExceptionDetail, Boolean editFlag, UserDTO userDTO) {
		if (!editFlag) {
			// TODO add
		} else {
			// TODO edit
		}
		vaildateModelRepeated(taskExceptionDetail, editFlag, userDTO);
	}

	private void vaildateModelRepeated(TaskExceptionDetail taskExceptionDetail, Boolean editFlag, UserDTO userDTO) {
		if (!editFlag) {
			// TODO add
		} else {
			// TODO
		}
	}

	private boolean canEdit(String pkId) {
		boolean flag = true;
		TaskExceptionDetail taskExceptionDetail = this.findById(pkId);
		AssertUtils.notNull(taskExceptionDetail, "model empty!");
		// TODO
		if (true)
			flag = false;
		return flag;
	}

	private Restriction buildCondition(TaskExceptionDetailDTO taskExceptionDetailDTO) {
		// TODO
		return null;
	}

	@Transactional
	@Override
	public Object saveTaskListByRq(List<RqcLinesDTO> rqcLinesDTOList) {
		// 保存质检的错误异常
		UserDTO userDTO = userUtils.getUserDTO();
		boolean editFlag = false;
		Date now = new Date();
		int row = 0;
		for (RqcLinesDTO rqcLinesDTO : rqcLinesDTOList) {
			row++;
			TaskExceptionDetail target = new TaskExceptionDetail();
//			BeanUtils.copyProperties(rqcLinesDTO, target);

			// =================新加 20220614
			String taskRqcId = rqcLinesDTO.getTaskRqcId();
			TaskQcDetail findById = this.taskQcMapper.findById(taskRqcId, TaskQcDetail.class);
			updateRqcTaskPoolStatus(taskRqcId, findById, row);
			BeanUtils.copyProperties(findById, target);
			// =================新加

			this.setBaseSysField(target, editFlag, userDTO, now); // 构建基础系统字段
			this.buildDefaultField(target, editFlag, userDTO); // 构建默认字段
			BigDecimal conversionRate = rqcLinesDTO.getConversionRate();
			BigDecimal pieceQty = rqcLinesDTO.getPieceQty();
			// 计算出
			if (null == conversionRate || conversionRate.compareTo(BigDecimal.ZERO) == 0) {
				target.setQty(pieceQty);
			} else {
				target.setQty(pieceQty.divide(conversionRate, 2, BigDecimal.ROUND_DOWN));
			}
			String lineRemark = rqcLinesDTO.getLineRemark();
			target.setComments(lineRemark);
			// 保存错误节点类型
			target.setNodeType(BizConstant.WMS_EXCEPTION_NODE_RQC);
			// 保存错误节点的任务id
			target.setNodeTaskId(taskRqcId);
			// 保存前校验来源任务id是否已有保存
			// 可多次生成异常
//			long count = this.mapper.findCountByMap(MapUtils.put("status", "0").put("nodeTaskId", taskRqcId).getMap(),
//					TaskExceptionDetail.class);
//			if (count > 0) {
//				throw new BizException(String.format("第[%d]行,错误节点任务id[%s],已生成过一次异常任务,请刷新重试!", row, taskRqcId));
//			}

			this.mapper.save(target);
		}
		return true;
	}

	private void updateRqcTaskPoolStatus(String taskRqcId, TaskQcDetail taskQcDetail, int row) {
		// 数据库乐观锁
		AssertUtils.notNull(taskQcDetail, String.format("第[%d]行,质检任务id:[%s]查询的任务为空!", row, taskRqcId));
		String status = taskQcDetail.getStatus();
		if (!"1".equals(status))
			throw new BizException(String.format("第[%d]行,质检任务id:[%s]查询的任务已经执行完毕,请刷新重试!", row, taskRqcId));
		Integer dataVersion = taskQcDetail.getDataVersion();

		// 这里只需要dataVersion其实就可以了
		Criteria criteria = new Criteria(TaskQcDetail.class);
		criteria.setRestriction(
				Restrictions.and(Restrictions.eq("taskRqcId", taskRqcId), Restrictions.eq("dataVersion", dataVersion)));
		// 置为不可用状态(异常)
		long count = this.taskQcMapper.updateMapByCriteria(
				MapUtils.put("dataVersion", dataVersion + 1).put("status", "0").getMap(), criteria);
		if (count <= 0)
			throw new BizException(String.format("第[%d]行,质检任务id:[%s]查询的任务执行中,请刷新重试!", row, taskRqcId));

	}

	@Transactional
	@Override
	public Object saveTaskListByInStock(List<RcvInstockLinesDTO> rcvInstockLinesDTOList) {
		// 保存上架异常任务池
		UserDTO userDTO = userUtils.getUserDTO();
		boolean editFlag = false;
		Date now = new Date();
		int row = 0;
		for (RcvInstockLinesDTO dto : rcvInstockLinesDTOList) {
			row++;
			TaskExceptionDetail target = new TaskExceptionDetail();
//			BeanUtils.copyProperties(dto, target);

			// =================新加 20220614
			String taskId = dto.getTaskId();
			TaskInstockDetail findById = this.taskInstockMapper.findById(taskId, TaskInstockDetail.class);
			updateInstockTaskPoolStatus(taskId, findById, row);
			BeanUtils.copyProperties(findById, target);
			// =================新加

			this.setBaseSysField(target, editFlag, userDTO, now); // 构建基础系统字段
			this.buildDefaultField(target, editFlag, userDTO); // 构建默认字段
			BigDecimal conversionRate = dto.getConversionRate();
			BigDecimal pieceQty = dto.getPieceQty();
			// 计算出
			if (null == conversionRate || conversionRate.compareTo(BigDecimal.ZERO) == 0) {
				target.setQty(pieceQty);
			} else {
				target.setQty(pieceQty.divide(conversionRate, 2, BigDecimal.ROUND_DOWN));
			}
			target.setComments(target.getComments());
			// 保存错误节点类型
			target.setNodeType(BizConstant.WMS_EXCEPTION_NODE_INSTOCK);
			// 保存错误节点的任务id
			target.setNodeTaskId(taskId);

			// 可多次生成任务
//			long count = this.mapper.findCountByMap(MapUtils.put("status", "0").put("nodeTaskId", taskId).getMap(),
//					TaskExceptionDetail.class);
//			if (count > 0) {
//				throw new BizException(String.format("第[%d]行,错误节点任务id[%s],已生成过一次异常任务,请刷新重试!", row, taskId));
//			}
			this.mapper.save(target);
		}
		return true;
	}

	private void updateInstockTaskPoolStatus(String taskId, TaskInstockDetail task, int row) {
		// 数据库乐观锁
		AssertUtils.notNull(task, String.format("第[%d]行,上架任务id:[%s]查询的任务为空!", row, taskId));
		String status = task.getStatus();
		if (!"1".equals(status))
			throw new BizException(String.format("第[%d]行,上架任务id:[%s]查询的任务已经执行完毕,请刷新重试!", row, taskId));

		Integer dataVersion = task.getDataVersion();

		// 这里只需要dataVersion其实就可以了
		Criteria criteria = new Criteria(TaskInstockDetail.class);
		criteria.setRestriction(Restrictions.and(Restrictions.eq("taskInboundId", taskId),
				Restrictions.eq("dataVersion", dataVersion)));
		// 置为不可用状态(异常)
		long count = this.taskInstockMapper.updateMapByCriteria(
				MapUtils.put("dataVersion", dataVersion + 1).put("status", "0").getMap(), criteria);
		if (count <= 0)
			throw new BizException(String.format("第[%d]行,上架任务id:[%s]查询的任务执行中,请刷新重试!", row, taskId));

	}

	private void buildDefaultField(TaskExceptionDetail target, boolean editFlag, UserDTO userDTO) {
		// 构建默认字段
		String activeFlag = target.getActiveFlag();
		if (!editFlag && ObjectUtils.isNull(activeFlag)) {
			target.setActiveFlag("1");
		}
		String executor = target.getExecutor();
		if (!editFlag && ObjectUtils.isNull(executor)) {
			target.setExecutor(userDTO.getUsername());
		}
		String nodeType = target.getNodeType();
		if (!editFlag && ObjectUtils.isNull(nodeType)) {
			target.setNodeType("20"); // 质检节点错误
		}
		String status = target.getStatus();
		if (!editFlag && ObjectUtils.isNull(status)) {
			target.setStatus("1");
		}
		if (!editFlag) {
			target.setDataVersion(0);
		}

	}

}
