/*
 *  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.youchain.basicdata.domain.Box;
import com.youchain.basicdata.domain.Item;
import com.youchain.basicdata.domain.Point;
import com.youchain.basicdata.domain.Stock;
import com.youchain.basicdata.repository.BoxRepository;
import com.youchain.basicdata.repository.StockRepository;
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.AgvTaskRepository;
import com.youchain.businessdata.repository.InventoryRepository;
import com.youchain.businessdata.repository.TaskRepository;
import com.youchain.businessdata.service.InventoryService;
import com.youchain.businessdata.service.TaskService;
import com.youchain.businessdata.service.dto.InventoryDto;
import com.youchain.exception.handler.ApiResult;
import com.youchain.modules.quartz.utils.TimeNumberUtils;
import com.youchain.modules.system.domain.Dept;
import com.youchain.utils.*;
import lombok.RequiredArgsConstructor;
import com.youchain.businessdata.repository.PickDetailRepository;
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.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.text.SimpleDateFormat;
import java.util.*;
import java.io.IOException;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpServletResponse;

import static org.springframework.http.HttpStatus.BAD_REQUEST;

/**
 * @author huojin
 * @website https://eladmin.vip
 * @description 服务实现
 * @date 2023-08-16
 **/
@Service
@RequiredArgsConstructor
public class PickDetailServiceImpl implements PickDetailService {
    private final PickDetailRepository pickDetailRepository;
    private final PickDetailMapper pickDetailMapper;
    private final InventoryService inventoryService;
    private final BoxRepository boxRepository;
    private final AgvTaskRepository agvTaskRepository;
    private final TaskService taskService;
    private final ItemService itemService;
    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 synchronized void allocate(long id, double quantity,String areaName) throws Exception {
        PickDetailDto pickDetailDto = findById(id);
        PickDetail pd = toEntity(pickDetailDto);//Dto转实体
        ItemDto itemDto = pickDetailDto.getItem();
        Item item = itemService.toEntity(itemDto);
        Dept dept = item.getDept();//仓库
        Box box = boxRepository.getBoxByItem(item.getId());
        Point endPoint = box.getPoint();//目标点位
        if (pd.getOrderQty() > pd.getAllocatedQty()) {
            List<Inventory> Inventorys = inventoryService.queryInventory(item.getId(),areaName);
            if (Inventorys.size() > 0) {
                double allocateQty = 0;
                double unQty = quantity;//未分配数量
                for (Inventory inv : Inventorys) {
                    Stock stock = inv.getStock();//容器
                    Point startPoint = inv.getPoint();//起始点位
                    if (unQty == 0) {
                        break;
                    }
                    allocateQty = inv.getQuantity() - inv.getQueuedQty();//库存可用数量
                    if (allocateQty <= 0) {
                        continue;
                    }
                    if (unQty < allocateQty) {
                        allocateQty = unQty;
                    }
                    inv.setQueuedQty(inv.getQueuedQty() + allocateQty);
                    inventoryService.update(inv);
                    unQty -= allocateQty;
                    pd.setAllocatedQty(pd.getAllocatedQty() + allocateQty);
                    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, "01");
                    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(allocateQty);
                    task.setInvId(inv.getId());
                    task.setDept(dept);
                    task.setAgvTask(agvTask);
                    taskService.create(task);
                }
            }

        } else {
            throw new RuntimeException("已分配,请勿重复操作！");
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void allocateAll(long id, Point endPoint,String areaName) 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 = inventoryService.queryInventory(item.getId(),areaName);
        if (Inventorys.size() > 0) {
            Inventory inv = Inventorys.get(0);
            //库存冻结状态不允许出库
            if (inv.getBeLock()) {
                throw new RuntimeException("库存锁定，不允许出库！");
            }
            Stock stock = inv.getStock();//容器
            Point startPoint = inv.getPoint();//起始点位
            inv.setQueuedQty(inv.getQuantity());
            inventoryService.update(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, "");
            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.setInvId(inv.getId());
            task.setDept(dept);
            task.setAgvTask(agvTask);
            task.setTaskType(BizStatus.PICK);
            taskService.create(task);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void cancelAllocate(long id, double quantity) throws Exception {

        PickDetailDto pickDetailDto = findById(id);
        PickDetail pd = toEntity(pickDetailDto);
        List<Task> Tasks = taskService.getPickNotAllTask(id);
        for (Task task : Tasks) {
            //根据Taskz找到对应的库存
            InventoryDto inventoryDto = inventoryService.findById(task.getInvId());
            Inventory inv=inventoryService.toEntity(inventoryDto);
            inv.setQueuedQty(inv.getQueuedQty() - pd.getAllocatedQty());
            inventoryService.update(inv);

            //删除Task
            taskService.delete(task);

            pd.setAllocatedQty(pd.getAllocatedQty() - quantity);
            if (pd.getAllocatedQty() == 0) {
                pd.setStatus(BizStatus.OPEN);
            }
            pickDetailRepository.save(pd);

        }

    }

    @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());
        if(StringUtils.isEmpty(po)){
            po= TimeNumberUtils.getCKCode();
        }
        d.setPo(po);
        pickDetailRepository.save(d);
        return d;
    }

    @Override
    public void save(Pick pick) {
        GdDetail gdDetail=pick.getGdDetail();
        gdDetail.getItem();
    }
}
