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

import com.fasterxml.jackson.core.JsonProcessingException;
import com.prolog.cs.basic.warehouse.model.vo.ContainerQueryVO;
import com.prolog.cs.biz.inbound.constant.OperationConstant;
import com.prolog.cs.biz.inbound.dao.failure.ExceptionHeadersMapper;
import com.prolog.cs.biz.inbound.dto.PageParam;
import com.prolog.cs.biz.inbound.dto.failure.*;
import com.prolog.cs.biz.inbound.dto.rcv.RcvHDTO;
import com.prolog.cs.biz.inbound.dto.task.TaskExceptionDetailDTO;
import com.prolog.cs.biz.inbound.model.failure.ExceptionHeaders;
import com.prolog.cs.biz.inbound.remote.BaseWmsWarehouseCaller;
import com.prolog.cs.biz.inbound.service.failure.ExceptionHeadersService;
import com.prolog.cs.biz.inbound.service.failure.ExceptionLinesService;
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.dao.util.PageUtils;
import com.prolog.framework.utils.AssertUtils;
import com.prolog.framework.utils.JsonUtils;
import com.prolog.framework.utils.SnowFlakeUtils;
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 javax.annotation.Resource;
import javax.validation.Validator;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ExceptionHeadersServiceImpl extends BaseServiceImpl<ExceptionHeaders> implements ExceptionHeadersService {

	@Resource
	private ExceptionHeadersMapper mapper;

	@Resource
	private UserUtils userUtils;

	@Resource
	private TaskExceptionDetailService taskExceptionDetailService;

	@Resource
	private ExceptionLinesService exceptionLinesService;

	@Resource
	private Validator validator;

	@Autowired
	private BaseWmsWarehouseCaller baseWmsWarehouseCaller;

	private static final String SERVICE_MSG = "异常头业务";
	private static Logger log = LoggerFactory.getLogger(ExceptionHeadersServiceImpl.class);

	@Transactional
	@Override
	public Object addWithRelated(ExceptionCascadeDTO cascadeDTO) {
		// 级联保存关联信息
		ExceptionHeadersDTO exceptionHeader = cascadeDTO.getExceptionHeader();
		AssertUtils.notNull(exceptionHeader, SERVICE_MSG + "级联保存单头信息不可为空!");
		// 记录日志
		try {
			log.info("开始级联保存异常信息:[{}]", JsonUtils.toString(exceptionHeader));
		} catch (JsonProcessingException e) {
		}
		UserDTO userDTO = userUtils.getUserDTO();
		Date now = new Date();
		// step1: 保存异常主表
		String masterId = this.add(exceptionHeader, userDTO);

		List<ExceptionLinesDTO> exceptionLineList = cascadeDTO.getExceptionLineList();
		// step2: 保存异常行
		if (ObjectUtils.isNotNull(exceptionLineList))
			exceptionLinesService.saveList(exceptionLineList, masterId, userDTO, now, true);

		//如果数量为零，则释放容器
		List<String> zeroContainerNos = exceptionLineList.stream().filter(item -> item.getPieceQtyNew().compareTo(BigDecimal.ZERO) == 0).map(ExceptionLinesDTO::getContainerCode).collect(Collectors.toList());
		if(zeroContainerNos != null && zeroContainerNos.size() > 0){
			//释放容器
			ContainerQueryVO containerQueryVO = new ContainerQueryVO();
			containerQueryVO.setContainerCodeList(zeroContainerNos);
			containerQueryVO.setWarehouseId(exceptionHeader.getWarehouseId());
			baseWmsWarehouseCaller.release(containerQueryVO);
		}
		return true;
	}

	@Override
	public Page<ExceptionHQueryDTO> findPage(ExceptionHQueryVO queryVO) {
		// 异常头分页查询
		queryVO.setEnterpriseId(userUtils.getEnterpriseId());
		String status = queryVO.getStatus();
		if (ObjectUtils.isNull(status))
			queryVO.setStatus("1"); // 默认可用的
		this.startPage(queryVO);
		List<ExceptionHQueryDTO> list = this.mapper.findList(queryVO);
		return PageUtils.getPage(list);
	}

	@Override
	public ExceptionHQueryDTO findByPkId(String pkId) {
		// 详情查询
		AssertUtils.isNotBlank(pkId, OperationConstant.PK_NULL_MSG + "(exceptionHeaderId)");
		return this.mapper.findByPkId(pkId);
	}

	@Override
	public Page<TaskExceptionDetailDTO> extractTask(TaskQueryVO taskQueryVO, PageParam pageParam) {
		// 异常任务池(待处理的)分页查询
		return taskExceptionDetailService.extractTaskWithNativeSql(taskQueryVO, pageParam);
	}

	@Override
	public Page<RcvHDTO> extractRcvHAndPro(RcvQueryVO rcvQueryVO, PageParam pageParam) {
		// 异常收货头信息
		return taskExceptionDetailService.extractRcvHWithNativeSql(rcvQueryVO, pageParam);
	}

	private String add(ExceptionHeadersDTO dto, UserDTO userDTO) {
		// 添加异常单头信息
		boolean editFlag = Boolean.FALSE;
		this.buildDefaultField(dto, userDTO);
		this.vaildateModel(dto, editFlag, userDTO, 1, validator, SERVICE_MSG + OperationConstant.ADD_OPERATION);
		ExceptionHeaders model = new ExceptionHeaders();
		BeanUtils.copyProperties(dto, model);
		this.setBaseSysField(model, editFlag, userDTO);

		this.mapper.save(model);
		return model.getExceptionHeaderId();
	}

	private void buildDefaultField(ExceptionHeadersDTO dto, UserDTO userDTO) {
		// 构建默认字段
		String autoBillNum = dto.getAutoBillNum();
		Date executorDate = dto.getExecutorDate();
		String executorUser = dto.getExecutorUser();
		if (ObjectUtils.isNull(autoBillNum))
			dto.setAutoBillNum(String.valueOf(SnowFlakeUtils.getId()));
		if (ObjectUtils.isNull(executorDate))
			dto.setExecutorDate(new Date());
		if (ObjectUtils.isNull(executorUser))
			dto.setExecutorUser(userDTO.getUsername());

	}

	@Override
	public Object add(ExceptionHeadersDTO exceptionHeadersDTO) {
		// TODO vaildate
		UserDTO userDTO = userUtils.getUserDTO();
		ExceptionHeaders exceptionHeaders = new ExceptionHeaders();
		BeanUtils.copyProperties(exceptionHeadersDTO, exceptionHeaders);
		this.vaildateModel(exceptionHeaders, false, userDTO);
		this.buildField(exceptionHeaders, false, userDTO);
		this.mapper.save(exceptionHeaders);
		return "";
	}

	@Override
	public Object edit(String pkId, ExceptionHeadersDTO exceptionHeadersDTO) {
		// TODO
		UserDTO userDTO = userUtils.getUserDTO();
		ExceptionHeaders exceptionHeaders = new ExceptionHeaders();
		BeanUtils.copyProperties(exceptionHeadersDTO, exceptionHeaders);
		this.vaildateModel(exceptionHeaders, true, userDTO);
		this.buildField(exceptionHeaders, true, userDTO);
		this.mapper.updateWithValidate(exceptionHeaders,
				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, ExceptionHeaders.class));
		return "";
	}

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

	@Override
	public Page<ExceptionHeadersDTO> findList(ExceptionHeadersDTO exceptionHeadersDTO, PageParam pageParam) {
		Criteria criteria = Criteria.forClass(ExceptionHeaders.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();
			ExceptionHeadersDTO condition = exceptionHeadersDTO;
			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<ExceptionHeaders> list = this.mapper.findByCriteria(criteria);
		List<ExceptionHeadersDTO> collect = Optional.ofNullable(list).orElse(Collections.emptyList()).stream()
				.map(p -> {
					ExceptionHeadersDTO target = new ExceptionHeadersDTO();
					BeanUtils.copyProperties(p, target);
					return target;
				}).collect(Collectors.toList());
		Page<ExceptionHeaders> page = PageUtils.getPage(list);
		Page<ExceptionHeadersDTO> 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<ExceptionHeadersDTO> exceptionHeadersDTOList) {
		// TODO
		UserDTO userDTO = userUtils.getUserDTO();
		for (ExceptionHeadersDTO exceptionHeadersDTO : exceptionHeadersDTOList) {
			ExceptionHeaders exceptionHeaders = new ExceptionHeaders();
			BeanUtils.copyProperties(exceptionHeadersDTO, exceptionHeaders);
			this.vaildateModel(exceptionHeaders, false, userDTO);
			this.buildField(exceptionHeaders, false, userDTO);
			this.mapper.save(exceptionHeaders);
		}
		return "";
	}

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

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

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

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

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

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

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

	private Restriction buildCondition(ExceptionHeadersDTO exceptionHeadersDTO) {
		// TODO
		return null;
	}

	/**
	 * 批号错误驳回
	 */
	@Override
	@Transactional
	public Object rejectLotNo(List<ExceptionLinesDTO> exceptionLineList) {
		AssertUtils.isNotEmpty(exceptionLineList, "请选择需要驳回的错误行!");
		UserDTO userDTO = userUtils.getUserDTO();
		ExceptionHeadersDTO exceptionHeader = new ExceptionHeadersDTO();
		BeanUtils.copyProperties(exceptionLineList.get(0), exceptionHeader);
		exceptionHeader.setExecutorUser(userDTO.getUsername());

		Date now = new Date();
		// step1: 保存异常主表
		String masterId = this.add(exceptionHeader, userDTO);

		// step2: 保存异常行
		if (ObjectUtils.isNotNull(exceptionLineList))
			exceptionLinesService.saveList(exceptionLineList, masterId, userDTO, now, false);

//		Set<String> batchIdSet = exceptionLineList.stream().map(ExceptionLinesDTO::getBatchIdOld)
//				.collect(Collectors.toSet());
//		Map<String, Object> result = excuteVaildateHasInstock(batchIdSet);
//		int row = 0;
//		for (ExceptionLinesDTO lineDTO : exceptionLineList) {
//			row++;
//			boolean flag = (boolean) result.get(lineDTO.getBatchIdOld());
//			if (flag) {
//				throw new BizException(String.format("第[%d]行,批号[%s]已有库存信息,无法驳回,若要修改,请联系相关人员去批次调整中进行修改!", row,
//						lineDTO.getManufacturerLotNumOld()));
//			}
//		}
		// 校验成功
		// 清理所有和此batchId相关的记录的数据,数量置为0,状态为作废状态
		return exceptionLinesService.rejectLotNo(exceptionLineList);
	}

	private Map<String, Object> excuteVaildateHasInstock(Set<String> batchIdSet) {
		// TODO Auto-generated method stub

		return null;
	}

	/**
	 * 是否可修改批号信息
	 */
	@Override
	public Object hasInstock(String batchId, String warehouseId) {
		AssertUtils.isNotEmpty(batchId, "请选择需要校验的异常行(batchId)!");
		AssertUtils.isNotEmpty(warehouseId, "仓库id为空(warehouseId)!");
		// 需要远程调用库存服务
		return exceptionLinesService.hasInstock(batchId, warehouseId);
	}

	/**
	 * 异常取消
	 */
	@Override
	public Object cancel(List<ExceptionLinesDTO> exceptionLineList) {
		AssertUtils.isNotEmpty(exceptionLineList, "请选择需要的错误行!");
		return exceptionLinesService.cancel(exceptionLineList);
	}

}
