/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.youchain.businessdata.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.youchain.basicdata.domain.*;
import com.youchain.basicdata.repository.*;
import com.youchain.basicdata.service.ItemService;
import com.youchain.basicdata.service.PointService;
import com.youchain.basicdata.service.dto.ItemDto;
import com.youchain.businessdata.domain.*;
import com.youchain.businessdata.repository.*;
import com.youchain.businessdata.service.AgvTaskService;
import com.youchain.exception.handler.ApiResult;
import com.youchain.modules.system.domain.Dept;
import com.youchain.utils.*;
import lombok.RequiredArgsConstructor;
import com.youchain.businessdata.service.PickDetailService;
import com.youchain.businessdata.service.dto.PickDetailDto;
import com.youchain.businessdata.service.dto.PickDetailQueryCriteria;
import com.youchain.businessdata.service.mapstruct.PickDetailMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.io.IOException;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpServletResponse;

/**
 * @author huojin
 * @website https://eladmin.vip
 * @description 服务实现
 * @date 2023-08-16
 **/
@Service
@RequiredArgsConstructor
@Transactional
public class PickDetailServiceImpl implements PickDetailService {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH");
    private final PickDetailRepository pickDetailRepository;
    private final PointRepository pointRepository;
    private final AgvTaskService agvTaskService;
    private final PickRepository pickRepository;
    private final PickDetailMapper pickDetailMapper;
    private final BillTypeRepository billTypeRepository;
    private final InventoryRepository inventoryRepository;
    private final BoxRepository boxRepository;
    private final AgvTaskRepository agvTaskRepository;
    private final TaskRepository taskRepository;
    private final ItemService itemService;
    private final PointService pointService;
    private final StockRepository stockRepository;
    private final ItemRepository itemRepository;
    private final EntityManager entityMapper;

    @Override
    public Map<String, Object> queryAll(PickDetailQueryCriteria criteria, Pageable pageable) {
        Page<PickDetail> page = pickDetailRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(pickDetailMapper::toDto));
    }

    @Override
    public List<PickDetailDto> queryAll(PickDetailQueryCriteria criteria) {
        return pickDetailMapper.toDto(pickDetailRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    @Transactional
    public PickDetailDto findById(Long id) {
        PickDetail pickDetail = pickDetailRepository.findById(id).orElseGet(PickDetail::new);
        ValidationUtil.isNull(pickDetail.getId(), "PickDetail", "id", id);
        return pickDetailMapper.toDto(pickDetail);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PickDetailDto create(PickDetail resources) {
        return pickDetailMapper.toDto(pickDetailRepository.save(resources));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(PickDetail resources) {
        PickDetail pickDetail = pickDetailRepository.findById(resources.getId()).orElseGet(PickDetail::new);
        ValidationUtil.isNull(pickDetail.getId(), "PickDetail", "id", resources.getId());
        pickDetail.copy(resources);
        pickDetailRepository.save(pickDetail);
    }

    @Override
    public void deleteAll(Long[] ids) {
        for (Long id : ids) {
            pickDetailRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<PickDetailDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        int i = 1;
        for (PickDetailDto pickDetail : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("序号", i++);
            map.put("任务号", pickDetail.getPo());
            map.put("物料编码", pickDetail.getItem().getCode());
            map.put("物料名称", pickDetail.getItem().getName());
            map.put("状态", pickDetail.getStatus());
            map.put("订单数量", pickDetail.getOrderQty());
            map.put("分配数量", pickDetail.getAllocatedQty());
            map.put("拣货数量", pickDetail.getPickedQty());
            map.put("备注", pickDetail.getRemark());
            map.put("创建人", pickDetail.getCreateBy());
            map.put("创建时间", pickDetail.getCreateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResult allocate(Long[] ids) {
        for (int i = 0; i < ids.length; i++) {
            Long id=ids[i];
            PickDetail pickDetail=pickDetailRepository.getById(id);
            Item item = pickDetail.getItem();
            Dept dept = item.getDept();//仓库
            Double quantity=pickDetail.getOrderQty()-pickDetail.getAllocatedQty();//未分配数量
            ApiResult apiResult =null;
            if (pickDetail.getPropC4()==null){
                /** 默认出库配置*/
                List<Box> boxList=boxRepository.findByCodeAndItem(BizStatus.BCP_PT);
                if (boxList.size()>0){
                    Box box=boxList.get(0);
                    pickDetail.setPropC4(box.getLampCode());
                    pickDetail.setPropC5(box.getPoint().getCode());
                    pickDetailRepository.save(pickDetail);
                }else {
                    return ApiResult.fail(500, "请设置业务类型", "");
                }
            }
            if (pickDetail.getPropC4().equals(BizStatus.RG_PT)){
                /** 人工出库 */
                apiResult = getAllocate(pickDetail, item, dept, quantity,0);
            }
            if (pickDetail.getPropC4().equals(BizStatus.AGV_PT)){
                /** AGV出库分配 */
                //获取目标点位
                apiResult = getAllocate(pickDetail, item, dept, quantity,1);
            }
            Pick pick=pickDetail.getPick();
            pick.setStatus(BizStatus.ALLOCATE);
            pickRepository.save(pick);
            if (apiResult != null) return apiResult;
        }
        return ApiResult.fail(200,"操作成功",null);
    }

    private ApiResult getAllocate(PickDetail pickDetail, Item item, Dept dept, Double quantity,Integer agvFlag) {
        Point endPoint;
        endPoint = pointRepository.findByCode(pickDetail.getPropC5());
        if (pickDetail.getOrderQty() > pickDetail.getAllocatedQty()) {
            List<Inventory> inventoryList = inventoryRepository.queryInventory(item.getId());
            if (inventoryList.size() > 0) {
                ApiResult apiResult = allocate(pickDetail, dept, quantity, agvFlag, endPoint, inventoryList);
                if (apiResult != null) return apiResult;
            }
            else {
                return ApiResult.fail(500, item.getCode() + "料号，无库存", "");
            }
        }else {
            return ApiResult.fail(500, "已分配", "");
        }
        return null;
    }

    public ApiResult allocate(PickDetail pickDetail, Dept dept, Double quantity, Integer agvFlag, Point endPoint, List<Inventory> inventoryList) {
        double allocateQty;//待分配数量
        double unQty = quantity;//订单总量
        for (Inventory inv : inventoryList) {
            Point startPoint = inv.getPoint();//原库位
            if (unQty <= 0) {
                break;
            }
            //整托分配
            allocateQty = inv.getQuantity();
            if (unQty < allocateQty) {
                return ApiResult.fail(201, "单号:" + pickDetail.getPick().getRelatedBill1()
                        + "行:" + pickDetail.getLineNo() + ",剩余订单数量不足一托" + "订单数量" + quantity + ",剩余未分配数量"
                        + unQty + ",库存单托数量" + inv.getQuantity(), null);
            }
            //更新库存
            inv.setQueuedQty(allocateQty);
            inventoryRepository.save(inv);
            unQty = unQty-allocateQty;
            //更新出库单
            pickDetail.setAllocatedQty(pickDetail.getAllocatedQty() + allocateQty);
            pickDetail.setStatus(BizStatus.ALLOCATE);
            pickDetailRepository.save(pickDetail);
            //生成Task任务
            Task task = new Task();
            task.setItem(inv.getItemKey().getItem());
            task.setItemKey(inv.getItemKey());
            task.setPickDetail(pickDetail);
            task.setBillCode(pickDetail.getPick().getRelatedBill1());
            task.setSrcPoint(startPoint);
            task.setDstPoint(endPoint);
            task.setTaskType(pickDetail.getPick().getBillType().getCode());
            task.setTaskStatus(BizStatus.ALLOCATE);
            task.setBeSkip(agvFlag);//是否AGV出库
            task.setPlanQty(allocateQty);
            task.setDept(dept);
            task.setInventory(inv);
            if (agvFlag ==1) {
                /** 是否AGV任务*/
                if (task.getTaskType().equals(BizStatus.RM_CK)){
                    // 如果是料箱出库，先不生成搬运任务
                }else {
                    //生成AGV任务
                    AgvTask agvTask = agvTaskService.addAgvTask(BizStatus.AGV, startPoint.getStorageType(), startPoint.getCode(), endPoint.getCode(), BizStatus.OPEN, BizStatus.PICK);
                    //更新Task任务 写入关联AGV
                    task.setAgvTask(agvTask);
                }
            }
            taskRepository.save(task);
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void allocateAll(long id, Point endPoint) throws Exception {
        PickDetailDto pickDetailDto = findById(id);
        PickDetail pd = toEntity(pickDetailDto);//Dto转实体
        ItemDto itemDto = pickDetailDto.getItem();
        Item item = itemService.toEntity(itemDto);
        Dept dept = item.getDept();//仓库
        List<Inventory> Inventorys = inventoryRepository.queryInventory(item.getId());
        if (Inventorys.size() > 0) {
            Inventory inv = Inventorys.get(0);
            if(item.getName().equals("后桶")){
               double hh=item.getExtendD2();
               if(hh!=0){
                   //判断库存距离生产批次超过4小时
                   String startDateTimeStr = dateFormat.format(inv.getCreateTime());
                   Date date = new Date();
                   String endDateTimeStr = dateFormat.format(date);
                   long hours = DateUtil.hoursBetween(startDateTimeStr, endDateTimeStr);
                   if (hours <= hh) {
                       throw new RuntimeException("生产批次未超过"+hh+"小时，不允许出库！");
                   }
               }

            }
            //库存冻结状态不允许出库
            if (inv.getBeLock()) {
                throw new RuntimeException("库存锁定，不允许出库！");
            }
            Stock stock = inv.getStock();//容器
            Point startPoint = inv.getPoint();//起始点位
            inv.setQueuedQty(inv.getQuantity());
            inventoryRepository.save(inv);
            pd.setAllocatedQty(inv.getQuantity());
            pd.setOrderQty(pd.getAllocatedQty());
            if (pd.getOrderQty().equals(pd.getAllocatedQty())) {
                pd.setStatus(BizStatus.ALLOCATE);
            }
            pickDetailRepository.save(pd);

            //生成搬运任务
            AgvTask agvTask = new AgvTask(BizStatus.PICK, stock.getCode(), startPoint.getCode(), endPoint.getCode(), BizStatus.OPEN, "RACK_MOVE_FULL");
            agvTaskRepository.save(agvTask);

            //生成Task
            Task task = new Task();
            task.setItem(item);
            task.setItemKey(inv.getItemKey());
            task.setPickDetail(pd);
            task.setBillCode(pd.getPo());
            task.setSrcStock(inv.getStock());
            task.setDstStock(inv.getStock());
            task.setSrcPoint(startPoint);
            task.setDstPoint(endPoint);
            task.setSrcStockCode(inv.getStock().getCode());
            task.setSrcPointCode(startPoint.getCode());
            task.setDstPointCode(endPoint.getCode());
            task.setInvStatus(inv.getStatus());
            task.setTaskStatus(BizStatus.OPEN);
            task.setPlanQty(inv.getQuantity());
            task.setDept(dept);
            task.setAgvTask(agvTask);
            task.setTaskType(BizStatus.PICK);
            taskRepository.save(task);

        }


    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void xjAllocateAll(long id, Point endPoint) throws Exception {
        PickDetailDto pickDetailDto = findById(id);
        PickDetail pd = toEntity(pickDetailDto);//Dto转实体
        ItemDto itemDto = pickDetailDto.getItem();
        Item item = itemService.toEntity(itemDto);
        Dept dept = item.getDept();//仓库
        List<Inventory> Inventorys = inventoryRepository.queryInventory(item.getId());
        if (Inventorys.size() > 0) {
            Inventory inv = Inventorys.get(0);
            //库存冻结状态不允许出库
            if (inv.getBeLock()) {
                throw new RuntimeException("库存锁定，不允许出库！");
            }
            Stock stock = inv.getStock();//容器
            stock.setLineSlotCode(endPoint.getCode());//按钮盒线边点位
            stockRepository.save(stock);
            Point startPoint = inv.getPoint();//起始点位
            inv.setQueuedQty(inv.getQuantity());
            inventoryRepository.save(inv);
            pd.setAllocatedQty(inv.getQuantity());
            pd.setOrderQty(pd.getAllocatedQty());
            if (pd.getOrderQty().equals(pd.getAllocatedQty())) {
                pd.setStatus(BizStatus.ALLOCATE);
            }
            pickDetailRepository.save(pd);

            //缓存输送线目标点 写死
            Point endPoint2 = pointService.getPoint("",BaseStatus.FREE,BaseStatus.BOX,"出库缓存输送线满箱接驳口");
            if (endPoint2 == null) {
                throw new RuntimeException("出库缓存输送线满箱接驳口点位没有维护！");
            }
            //生成搬运任务
            AgvTask agvTask = new AgvTask(BizStatus.PICK, stock.getCode(), startPoint.getCode(), endPoint2.getCode(), BizStatus.OPEN, "PICKER_MOVE");
            agvTask.setLineSlotCode(endPoint.getCode());
            agvTaskRepository.save(agvTask);


            //生成Task
            Task task = new Task();
            task.setItem(item);
            task.setItemKey(inv.getItemKey());
            task.setPickDetail(pd);
            task.setBillCode(pd.getPo());
            task.setSrcStock(inv.getStock());
            task.setDstStock(inv.getStock());
            task.setSrcPoint(startPoint);
            task.setDstPoint(endPoint);
            task.setSrcStockCode(inv.getStock().getCode());
            task.setSrcPointCode(startPoint.getCode());
            task.setDstPointCode(endPoint.getCode());
            task.setInvStatus(inv.getStatus());
            task.setTaskStatus(BizStatus.OPEN);
            task.setPlanQty(inv.getQuantity());
            task.setDept(dept);
            task.setAgvTask(agvTask);
            task.setTaskType(BizStatus.PICK);
            taskRepository.save(task);

        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResult cancelAllocate(long id, double cancelQuantity){

        PickDetailDto pickDetailDto = findById(id);
        PickDetail pd = toEntity(pickDetailDto);
        List<Task> Tasks = taskRepository.getPickNotAllTask(id);
        for (Task task : Tasks) {
            if (cancelQuantity<=0){
                break;
            }
            cancelQuantity=cancelQuantity-task.getPlanQty();
            //根据Task找到对应的库存
            Inventory inv = inventoryRepository.findById(task.getInventory().getId()).get();
            inv.setQueuedQty(inv.getQueuedQty() - task.getPlanQty());
            inventoryRepository.save(inv);
            // 更新库位状态
            Point point=inv.getPoint();
            point.setStatus(BaseStatus.USED);
            pointRepository.save(point);
            //删除AgvTask任务
            if (task.getAgvTask()!=null){
                AgvTask agvTask=task.getAgvTask();
                if (agvTask.getStatus().equals(BizStatus.OPEN)){
                    agvTaskRepository.delete(agvTask);
                }else {
                    return ApiResult.fail(400, "搬运任务已执行", null);
                }
            }
            //删除Task
            taskRepository.delete(task);

            pd.setAllocatedQty(pd.getAllocatedQty() - task.getPlanQty());
            if (pd.getAllocatedQty() == 0) {
                pd.setStatus(BizStatus.OPEN);
            }
            pickDetailRepository.save(pd);
        }
        return ApiResult.fail(200, "操作成功", null);
    }

    @Override
    public ApiResult cancelAllocate(Long[] ids) {
        ApiResult apiResult=new ApiResult();
        for (int i = 0; i < ids.length; i++) {
            PickDetail pickDetail=pickDetailRepository.getById(ids[i]);
            apiResult = cancelAllocate(pickDetail.getId(), pickDetail.getAllocatedQty());

        }
        return apiResult;
    }

    @Override
    public long count() {
        return pickDetailRepository.count();
    }

    @Override
    public PickDetail toEntity(PickDetailDto pickDetailDto) {
        return pickDetailMapper.toEntity(pickDetailDto);
    }

    public List<Object[]>  queryOut(String type,String date){
        String hql="";
        if(type.equals("Day")){
            hql="select sum(d.orderQty),sum(d.pickedQty) from PickDetail d " +
                    " where DATE_FORMAT(d.createTime,'%Y-%m-%d')=DATE_FORMAT(now(),'%Y-%m-%d')";
        }else if(type.equals("Month")){
            hql="select sum(d.orderQty),sum(d.pickedQty) from PickDetail d " +
                    " where DATE_FORMAT(d.createTime,'%Y-%m')=DATE_FORMAT(now(),'%Y-%m')";
        }else if(type.equals("InOutDay")){
            hql="select DATE_FORMAT(d.createTime,'%Y-%m-%d'),sum(d.pickedQty) from PickDetail d " +
                    " where DATE_FORMAT(d.createTime,'%Y-%m-%d')>='"+date+"' " +
                    " group by DATE_FORMAT(d.createTime,'%Y-%m-%d')";
        }
        Query query=entityMapper.createQuery(hql);
        List<Object[]> ts=query.getResultList();
        return  ts;
    }

    @Override
    public PickDetail createPickDetail(Item item,String po) {
        PickDetail d= new PickDetail();
        d.setItem(item);
        d.setLineNo(1l);
        d.setStatus(BizStatus.OPEN);
        d.setOrderQty(item.getExtendD1());
        d.setDept(item.getDept());
        d.setPo(po);
        pickDetailRepository.save(d);
        return d;
    }

    @Override
    public ApiResult importPtApi(JSONObject jsonObject) {
        JSONArray orders=jsonObject.getJSONArray("order");
        for (int i = 0; i < orders.size(); i++) {
            JSONObject order=orders.getJSONObject(i);
            String orderId=order.getString("orderId");
            String erTime=order.getString("orderDate");//操作时间
            Timestamp er = Timestamp.valueOf(DateUtil.parseLocalDateTimeFormat(erTime, "yyyy-MM-dd HH:mm:ss"));
            String owner=order.getString("owner");//货主
            String orderType=order.getString("orderType");//订单类型
            List<BillType> billTypeList=billTypeRepository.findBySourceName(orderType);
            if (billTypeList.size()<1){
                return ApiResult.result(500,"单据类型错误",null);
            }
            BillType billType=billTypeList.get(0);
            String dispatchDate=order.getString("dispatchDate");//发货日期
            Timestamp dc = Timestamp.valueOf(DateUtil.parseLocalDateTimeFormat(dispatchDate, "yyyy-MM-dd HH:mm:ss"));
            Long priority=order.getLong("priority");//订单类型
            String address=order.getString("consolidationLoc");//地址
            Pick pick;
            pick = pickRepository.findByCode(orderId);
            if (pick==null){
                pick=new Pick();
                pick.setStatus(BizStatus.OPEN);
            }
            pick.setRelatedBill1(orderId);
            pick.setRelatedBill2(orderId);
            pick.setErTime(er);
            pick.setBillType(billType);
            pick.setDispatchDate(dc);
            pick.setOwner(owner);
            pick.setAddress(address);
            pick.setPriority(priority);
            pickRepository.save(pick);
            JSONArray orderLineArray=order.getJSONArray("orderLine");
            for (int j = 0; j < orderLineArray.size(); j++) {
                JSONObject orderLine=orderLineArray.getJSONObject(i);
                String lineNo=orderLine.getString("orderLineNumber");
                String itemCode=orderLine.getString("productId");
                String quantity=orderLine.getString("quantityOrdered");
                JSONArray attributeValueList=orderLine.getJSONArray("attributeValue");
                Map<String,Object> attributeValue=new HashMap<>();
                for (int k = 0; k < attributeValueList.size(); k++) {
                    JSONObject at=attributeValueList.getJSONObject(k);
                    attributeValue.put(at.getString("name"),at.getString("value"));
                }
                String propC1=attributeValue.get("ztBidName")+"";//中标批次名称
                String propC2=attributeValue.get("prodMtrlNo")+"";//成品物料编码
                String propC3=attributeValue.get("loadUnitId")+"";//序列号
                Item item=itemRepository.findByCode(itemCode);
                if (item==null){
                    return ApiResult.result(500, itemCode+"物料不存在",null);
                }
                PickDetail pickDetail;
                pickDetail=pickDetailRepository.findByPickAndLineNo(pick.getId(),Long.valueOf(lineNo));
                if (pickDetail==null){
                    pickDetail=new PickDetail();
                    pickDetail.setStatus(BizStatus.OPEN);
                }
                pickDetail.setLineNo(Long.valueOf(lineNo));
                pickDetail.setItem(item);
                pickDetail.setOrderQty(Double.valueOf(quantity));
                pickDetail.setPropC1(propC1);
                pickDetail.setPropC2(propC2);
                pickDetail.setPropC3(propC3);
                pickDetail.setPick(pick);
                pickDetailRepository.save(pickDetail);
            }
        }
        return ApiResult.result(200, "操作成功",null);
    }

}
