package com.emergency.avenue.storage.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.emergency.avenue.admin.api.entity.SysDept;
import com.emergency.avenue.admin.api.entity.SysTreeDict;
import com.emergency.avenue.admin.api.vo.TreeBuilder;
import com.emergency.avenue.admin.api.vo.TreeNode;
import com.emergency.avenue.admin.mapper.SysTreeDictMapper;
import com.emergency.avenue.admin.service.SysDeptService;
import com.emergency.avenue.common.core.exception.BusinessException;
import com.emergency.avenue.common.core.util.R;
import com.emergency.avenue.common.security.service.AvenueUser;
import com.emergency.avenue.common.security.util.SecurityUtils;
import com.emergency.avenue.storage.api.entity.InboundInfo;
import com.emergency.avenue.storage.api.entity.InboundMaterialInfo;
import com.emergency.avenue.storage.api.entity.InboundStatus;
import com.emergency.avenue.storage.api.request.InboundInfoRequest;
import com.emergency.avenue.storage.api.request.StorageMaterialsRequest;
import com.emergency.avenue.storage.api.response.InboundInfoResponse;
import com.emergency.avenue.storage.api.response.StorageMaterialsResponse;
import com.emergency.avenue.storage.mapper.InboundMapper;
import com.emergency.avenue.storage.mapper.InboundMaterialMapper;
import com.emergency.avenue.storage.mapper.InboundStatusMapper;
import com.emergency.avenue.storage.service.InboundMaterialService;
import com.emergency.avenue.storage.util.MyFileUtil;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@AllArgsConstructor
public class InboundMaterialServiceImpl extends ServiceImpl<InboundMaterialMapper, InboundMaterialInfo> implements InboundMaterialService {

	private final InboundMaterialMapper inboundMaterialMapper;

	private final InboundStatusMapper inboundStatusMapper;

	private final InboundMapper inboundMapper;

	private final SysTreeDictMapper sysTreeDictMapper;

	private final SysDeptService sysDeptService;

	@Override
	public R<Map<Object, Object>> listMap(Integer id) {
		Map<Object, Object> objectObjectHashMap = new HashMap<>();
		List<InboundMaterialInfo> inboundMaterialInfos = inboundMaterialMapper.selectList(Wrappers.<InboundMaterialInfo>query().lambda().eq(InboundMaterialInfo::getInboundId, id));
		List<SysTreeDict> orderDictList = sysTreeDictMapper.getOrderDictList(1);
		Map<String, String> collect = orderDictList.stream().collect(Collectors.toMap(SysTreeDict::getId, SysTreeDict::getName));
		for (InboundMaterialInfo outboundMaterialInfo : inboundMaterialInfos) {
			outboundMaterialInfo.setMaterialGroup(collect.get(outboundMaterialInfo.getMaterialGroup()));
			outboundMaterialInfo.setMaterialType(collect.get(outboundMaterialInfo.getMaterialType()));
			outboundMaterialInfo.setMaterialName(collect.get(outboundMaterialInfo.getMaterialName()));
			outboundMaterialInfo.setUnit(collect.get(outboundMaterialInfo.getUnit()));
		}
		objectObjectHashMap.put("inboundMaterial", inboundMaterialInfos);
		QueryWrapper<InboundStatus> objectQueryWrapper = new QueryWrapper<>();
		objectQueryWrapper.lambda().eq(InboundStatus::getInboundId, id).orderByAsc(InboundStatus::getAuditingTime);
		List<InboundStatus> inboundStatus = inboundStatusMapper.selectList(objectQueryWrapper);
		Map<String, String> deptMap = sysDeptService.list(Wrappers.<SysDept>lambdaQuery())
				.stream().collect(Collectors.toMap(SysDept::getDeptId, SysDept::getAbbreviationName));
		inboundStatus.forEach(inboundStatus1 ->inboundStatus1.setWarehouse(deptMap.get(inboundStatus1.getDeptId())));
		objectObjectHashMap.put("inboundStatus", inboundStatus);
		return R.ok(objectObjectHashMap);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public R saveInboundStatus(InboundStatus inboundStatus) {
		String deptType = SecurityUtils.getUser().getDeptType();
		AvenueUser user = SecurityUtils.getUser();
		if (inboundStatus.getAudi().equals("0")) {
			if (deptType.equals("2")) {
				inboundStatus.setStatus("3");
			} else if (deptType.equals("1")) {
				inboundStatus.setStatus("5");
			}
		} else {
			if (deptType.equals("2")) {
				inboundStatus.setStatus("4");
			} else if (deptType.equals("1")) {
				inboundStatus.setStatus("6");
			}
		}
		inboundStatusMapper.updateByIdAndIsInvalid(inboundStatus.getInboundId());
		InboundInfo inboundInfo = new InboundInfo();
		inboundInfo.setStatus(inboundStatus.getStatus());
		inboundInfo.setInboundId(Integer.parseInt(inboundStatus.getInboundId()));
		inboundMapper.updateById(inboundInfo);
		inboundStatus.setAuditingTime(LocalDateTime.now());
		inboundStatus.setDeptId(user.getDeptId());
		inboundStatus.setUserId(user.getId());
		inboundStatusMapper.insert(inboundStatus);
		return R.ok("审核成功");
	}

	@Override
	public R<List<InboundInfoResponse>> selectListByDept(InboundInfoRequest inboundInfoRequest) {
		List<InboundInfoResponse> listInbound=null;
		if (inboundInfoRequest.getIsMaintenance()) {
			listInbound = inboundStatusMapper.selectListByDeptAndMaintenance(inboundInfoRequest);
		}else {
			listInbound = inboundStatusMapper.selectListByDept(inboundInfoRequest);
			// 防止多次扣减问题
			processInboundList(listInbound);
		}
		//List<InboundInfoResponse> listMoney = inboundStatusMapper.selectListByDeptMoney(inboundInfoRequest);
		// 第一行总价值 总数量
		List<InboundInfoResponse> listMoney=new LinkedList<>();
		// 按仓库分组
		Map<String, List<InboundInfoResponse>> collect1 = listInbound.stream().collect(Collectors.groupingBy(InboundInfoResponse::getWarehouseDeptId));
		for(String key:collect1.keySet()){
			// 一个仓库数据 整合计算总数  总价 & 总数
			List<InboundInfoResponse> inboundInfoResponses = collect1.get(key);
			InboundInfoResponse inboundInfoResponse=new InboundInfoResponse();
			inboundInfoResponse.setProvince(inboundInfoResponses.get(0).getProvince());
			inboundInfoResponse.setGameName(inboundInfoResponses.get(0).getGameName());
			inboundInfoResponse.setGameDeptId(inboundInfoResponses.get(0).getGameDeptId());
			inboundInfoResponse.setWarehouseName(inboundInfoResponses.get(0).getWarehouseName());
			inboundInfoResponse.setWarehouseDeptId(inboundInfoResponses.get(0).getWarehouseDeptId());
			inboundInfoResponse.setSort(1);
			BigDecimal reduce = inboundInfoResponses.stream().filter(in->in.getIsParts().equals("0")).map(InboundInfoResponse::getTotalPrice).map(BigDecimal::new).reduce(BigDecimal.ZERO, BigDecimal::add);
			inboundInfoResponse.setWarehouseTotalPrice(reduce);
			// 计算总数量  有特殊规则
			hande(inboundInfoResponses);
			//计算一个仓库的总数量
			BigDecimal warehouseTotalNum = inboundInfoResponses.stream().map(InboundInfoResponse::getWarehouseTotalNum).reduce(BigDecimal.ZERO, BigDecimal::add);
			inboundInfoResponse.setWarehouseTotalNum(warehouseTotalNum);
			listMoney.add(inboundInfoResponse);
		}
		if (CollUtil.isNotEmpty(listMoney)) {
			InboundInfoResponse inboundInfoResponse = new InboundInfoResponse();
			inboundInfoResponse.setGameDeptId("0");
			inboundInfoResponse.setWarehouseDeptId("0");
			inboundInfoResponse.setSort(0);
			inboundInfoResponse.setWarehouseTotalPrice(listMoney.stream().map(InboundInfoResponse::getWarehouseTotalPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
			BigDecimal acc = BigDecimal.ZERO;
			for (InboundInfoResponse infoResponse : listMoney) {
				BigDecimal warehouseTotalNum = infoResponse.getWarehouseTotalNum();
				acc = acc.add(warehouseTotalNum);
			}
			inboundInfoResponse.setWarehouseTotalNum(acc);
			listMoney.add(inboundInfoResponse);
		}
		listMoney.addAll(listInbound);

		// 列表排序
		List<InboundInfoResponse> collect = listMoney.stream()
				.sorted(Comparator.comparing(InboundInfoResponse::getGameDeptId, Comparator.comparingLong(s -> Optional.ofNullable(s).map(Long::parseLong).orElse(0L)))
				.thenComparing(InboundInfoResponse::getProvince, Comparator.nullsLast(Comparator.naturalOrder()))
				.thenComparing(InboundInfoResponse::getWarehouseDeptId, Comparator.comparingLong(s -> Optional.ofNullable(s).map(Long::parseLong).orElse(0L)))
				.thenComparingInt(InboundInfoResponse::getSort)
				.thenComparing(InboundInfoResponse::getWarehouseTotalPrice,Comparator.reverseOrder())
		).collect(Collectors.toList());
		return R.ok(collect);
	}

	public void hande(List<InboundInfoResponse> list3) {
        for (InboundInfoResponse bean : list3) {
            BigDecimal quantity = new BigDecimal(bean.getQuantity() == null ? "0" : bean.getQuantity());
            bean.setWarehouseTotalNum(quantity);
            switch (bean.getDmUpcode2()) {
                case "10305":
                    bean.setWarehouseTotalNum(quantity.multiply(BigDecimal.valueOf(10000L)).divide(BigDecimal.valueOf(500L), 6, BigDecimal.ROUND_HALF_UP));
                    break;

                case "10303":
                case "10306":
                    bean.setWarehouseTotalNum(quantity.multiply(BigDecimal.valueOf(10000L)).divide(BigDecimal.valueOf(600L), 6, BigDecimal.ROUND_HALF_UP));
                    break;

                case "10310":
                    bean.setWarehouseTotalNum(quantity.multiply(BigDecimal.valueOf(10000L)).divide(BigDecimal.valueOf(300L), 6, BigDecimal.ROUND_HALF_UP));
                    break;

                case "10307":
                    bean.setWarehouseTotalNum(quantity.divide(BigDecimal.valueOf(5L), 6, BigDecimal.ROUND_HALF_UP));
                    break;

                case "20121":
                case "20120":
                    bean.setWarehouseTotalNum(quantity.multiply(BigDecimal.valueOf(10000L)).divide(BigDecimal.valueOf(20L), 6, BigDecimal.ROUND_HALF_UP));
                    break;
            }
        }
	}

	@Override
	public R<List<InboundInfoResponse>> selectListByDeptMoney(InboundInfoRequest inboundInfoRequest) {
		List<InboundInfoResponse> list = inboundStatusMapper.selectListByDeptMoney(inboundInfoRequest);
		if (CollUtil.isNotEmpty(list)) {
			InboundInfoResponse inboundInfoResponse = new InboundInfoResponse();
			inboundInfoResponse.setGameDeptId("0");
			inboundInfoResponse.setWarehouseDeptId("0");
			inboundInfoResponse.setWarehouseTotalPrice(list.stream().map(InboundInfoResponse::getWarehouseTotalPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
			list.add(inboundInfoResponse);
		}
		return R.ok(list);
	}

	@Override
	public void exportFxkhwzsljzb(String fileName, String templateFile, List<InboundInfoResponse> list, HttpServletResponse response) {

		//拼合计数据到对应的列
		if(list  != null && !list.isEmpty()){
			for (int i = 0; i < list.size(); i++) {
				InboundInfoResponse inboundInfoResponse = list.get(i);
                if(inboundInfoResponse.getWarehouseTotalPrice() != null && inboundInfoResponse.getWarehouseTotalPrice().compareTo(BigDecimal.ZERO) != 0){
                    inboundInfoResponse.setTotalPrice(inboundInfoResponse.getWarehouseTotalPrice().toString());
                    inboundInfoResponse.setQuantity(inboundInfoResponse.getWarehouseTotalNum().toString());
					if (i == 0){
						inboundInfoResponse.setUnitPrice("总价值");
						inboundInfoResponse.setYears("总数量");
					}else{
						inboundInfoResponse.setUnitPrice("合计");
						inboundInfoResponse.setYears("合计");
					}
                }
			}
		}

//		根据模板生成文件
		List<Integer> strnumToNumColumnIndex = Arrays.asList(10,11,12);
		MyFileUtil.listToExcel(response,fileName, templateFile, 3,strnumToNumColumnIndex, list, null);
	}

	@Override
	public R<List<StorageMaterialsResponse>> selectListByStorageMaterials(StorageMaterialsRequest storageMaterialsRequest) {
		List<StorageMaterialsResponse> storageMaterialsResponses=null;
		if (storageMaterialsRequest.getIsDetails()) {
			if (StringUtils.isNotEmpty(storageMaterialsRequest.getDeptId())) {
				storageMaterialsResponses = inboundStatusMapper.selectListByStorageMaterialsDeptId(storageMaterialsRequest);
			}else {
				throw new BusinessException("请联系管理员处理");
			}
		} else{
			storageMaterialsResponses = inboundStatusMapper.selectListByStorageMaterials(storageMaterialsRequest);
	    }
		List<SysTreeDict> orderDictList = sysTreeDictMapper.selectList(Wrappers.<SysTreeDict>lambdaQuery().eq(SysTreeDict::getType,2).eq(SysTreeDict::getDifference,"1"));
		Map<String, String> collect = orderDictList.stream().collect(Collectors.toMap(SysTreeDict::getId, SysTreeDict::getName));
		storageMaterialsResponses.stream().forEach(storageMaterialsResponse -> storageMaterialsResponse.setProvinceName(collect.get(storageMaterialsResponse.getProvince())));
		return R.ok(storageMaterialsResponses);
	}

	@Override
	public List<TreeNode> getMaterialsTreeByWarehouseId(String warehouseId) {
		List<String> ids=new ArrayList<>();
		List<InboundMaterialInfo> inboundMaterialInfos = inboundMaterialMapper.selectListByWarehouseId(warehouseId);
		if (!inboundMaterialInfos.isEmpty()){
			 ids = inboundMaterialInfos.stream().flatMap(s -> Stream.of(s.getMaterialGroup(), s.getMaterialType(), s.getMaterialName(), s.getUnit()))
					 .filter(name -> name != null).collect(Collectors.toList());
		}
		if (!ids.isEmpty()){
			List<SysTreeDict> dictList = sysTreeDictMapper.getIds(ids);
			List<TreeNode> treeNodes = new ArrayList<>();
			dictList.stream()
					.forEach(dict -> treeNodes.add(JSON.parseObject(JSON.toJSONString(dict), TreeNode.class)));
			return TreeBuilder.buildByRecursive(treeNodes);
		}
		return   null;
	}

	/**
	 * 处理入库数据-价值表-：如果有两条唯一标识相同的(物资名称、入库时间、单价、是否可用、部门)入库数据，
	 * 防止多词扣减出库
	 * 处理逻辑：
	 * 	1. 按数量从小到大扣减，依次扣减，如果不够最后的一条为负数
	 * @param inboundList 入库信息列表
	 */
	public static void processInboundList(List<InboundInfoResponse> inboundList) {
		// 第一步：按唯一标识符分组
		Map<String, List<InboundInfoResponse>> groupedByUniqueId = inboundList.stream()
				.collect(Collectors.groupingBy(InboundInfoResponse::getUniqueIdentification));

		// 第二步：处理每个组
		for (Map.Entry<String, List<InboundInfoResponse>> entry : groupedByUniqueId.entrySet()) {
			List<InboundInfoResponse> sameUniqueIdList = entry.getValue();
			if (sameUniqueIdList.size()==1 || sameUniqueIdList.get(0).getQuantityOut().equals("0.0")){
				continue;
			}

			// 计算总出库数量和总价
			BigDecimal remainingQuantityOut = new BigDecimal(sameUniqueIdList.get(0).getQuantityOut());
			BigDecimal remainingTotalPriceOut = new BigDecimal(sameUniqueIdList.get(0).getTotalPriceOut());


			// 按数量升序排序
			sameUniqueIdList.sort(Comparator.comparing((InboundInfoResponse item) -> new BigDecimal(item.getQuantityIn())));

			// 第三步：扣减出库数量
			for (InboundInfoResponse item : sameUniqueIdList) {
				BigDecimal quantityIn = new BigDecimal(item.getQuantityIn());
				if (remainingQuantityOut.compareTo(quantityIn) >= 0) {
					remainingQuantityOut = remainingQuantityOut.subtract(quantityIn);
					item.setQuantity("0");
				} else {
					quantityIn = quantityIn.subtract(remainingQuantityOut);
					item.setQuantity(quantityIn.toString());
					remainingQuantityOut = BigDecimal.ZERO;
					break;
				}
			}

			// 第四步：扣减出库总价
			for (InboundInfoResponse item : sameUniqueIdList) {
				BigDecimal totalPriceIn = new BigDecimal(item.getTotalPriceIn());
				if (remainingTotalPriceOut.compareTo(totalPriceIn) >= 0) {
					remainingTotalPriceOut = remainingTotalPriceOut.subtract(totalPriceIn);
					item.setTotalPrice("0");
				} else {
					totalPriceIn = totalPriceIn.subtract(remainingTotalPriceOut);
					item.setTotalPrice(totalPriceIn.toString());
					remainingTotalPriceOut = BigDecimal.ZERO;
					break;
				}
			}

			// 如果仍有剩余的出库数量，将最后一个项目的数量设为负数
			if (remainingQuantityOut.compareTo(BigDecimal.ZERO) > 0) {
				InboundInfoResponse lastItem = sameUniqueIdList.get(sameUniqueIdList.size() - 1);
				BigDecimal lastQuantity = new BigDecimal(lastItem.getQuantity()).subtract(remainingQuantityOut);
				lastItem.setQuantity(lastQuantity.toString());
			}
			// 如果仍有剩余的出库数量，将最后一个项目的数量设为负数
			if (remainingTotalPriceOut.compareTo(BigDecimal.ZERO) > 0) {
				InboundInfoResponse lastItem = sameUniqueIdList.get(sameUniqueIdList.size() - 1);
				BigDecimal lastTotal = new BigDecimal(lastItem.getTotalPrice()).subtract(remainingTotalPriceOut);
				lastItem.setTotalPrice(lastTotal.toString());
			}
		}
	}
}
