/*
 *  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.JSON;
import com.youchain.basicdata.domain.Area;
import com.youchain.basicdata.domain.Item;
import com.youchain.basicdata.domain.Point;
import com.youchain.basicdata.domain.Stock;
import com.youchain.basicdata.repository.StockRepository;
import com.youchain.businessdata.returnJson.RPTaskList;
import com.youchain.businessdata.domain.*;
import com.youchain.businessdata.inputJson.IPTask;
import com.youchain.businessdata.repository.InventoryRepository;
import com.youchain.businessdata.repository.PickDetailRepository;
import com.youchain.businessdata.service.InventoryLogService;
import com.youchain.modules.system.domain.Dept;
import com.youchain.utils.*;
import lombok.RequiredArgsConstructor;
import com.youchain.businessdata.repository.TaskRepository;
import com.youchain.businessdata.service.TaskService;
import com.youchain.businessdata.service.dto.TaskDto;
import com.youchain.businessdata.service.dto.TaskQueryCriteria;
import com.youchain.businessdata.service.mapstruct.TaskMapper;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.SQLQuery;
import org.hibernate.transform.AliasToEntityMapResultTransformer;
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.util.*;
import java.io.IOException;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.servlet.http.HttpServletResponse;

/**
 * @author houjianlan
 * @website https://eladmin.vip
 * @description 服务实现
 * @date 2023-08-16
 **/
@Service
@RequiredArgsConstructor
@Slf4j
public class TaskServiceImpl implements TaskService {

    private final InventoryLogService inventoryLogService;
    private final TaskRepository taskRepository;
    private final TaskMapper taskMapper;
    private final InventoryRepository inventoryRepository;
    private final PickDetailRepository pickDetailRepository;
    private final EntityManager entityMapper;
    private  final StockRepository stockRepository;
    @Override
    public Map<String, Object> queryAll(TaskQueryCriteria criteria, Pageable pageable) {
        Page<Task> page = taskRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(taskMapper::toDto));
    }

    @Override
    public List<TaskDto> queryAll(TaskQueryCriteria criteria) {
        return taskMapper.toDto(taskRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    @Transactional
    public TaskDto findById(Long id) {
        Task task = taskRepository.findById(id).orElseGet(Task::new);
        ValidationUtil.isNull(task.getId(), "Task", "id", id);
        return taskMapper.toDto(task);
    }

    @Override
    @Transactional
    public Task getEntity(Long id) {
        Task task = taskRepository.findById(id).orElseGet(Task::new);
        return task;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TaskDto create(Task resources) {
        return taskMapper.toDto(taskRepository.save(resources));
    }

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

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

    @Override
    public void delete(Long id) {
        taskRepository.deleteById(id);
    }

    @Override
    public void download(List<TaskDto> all, HttpServletResponse response) throws Exception {
        List<Map<String, Object>>  list=ExcelDownUtils.CreateMap(all,"Task");
//        List<Map<String, Object>> list = new ArrayList<>();
//        for (TaskDto task : all) {
//            Map<String, Object> map = new LinkedHashMap<>();
//            map.put("序号", task.getId());
//            map.put("物料编码", task.getItemKey().getItem().getCode());
//            map.put("物料名称", task.getItemKey().getItem().getName());
//            map.put("容器编号", task.getSrcStock()==null?"":task.getSrcStock().getCode());
//            map.put("原点位", task.getSrcPointCode());
//            map.put("目标点位", task.getDstPointCode());
//            map.put("任务状态", task.getTaskStatus());
//            map.put("计划数量", task.getPlanQty());
//            map.put("移位数量", task.getMoveQty());
//            map.put("创建人", task.getCreateBy());
//            map.put("创建时间", task.getCreateTime());
//            list.add(map);
//        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public Task toEntity(TaskDto taskDto) {
        return taskMapper.toEntity(taskDto);
    }

    @Override
    public List<Task> getPickNotAllTask(Long id) {
        return taskRepository.getPickNotAllTask(id);
    }

    @Override
    public List<Task> getPickAllTask(Long id) {
        return taskRepository.getPickAllTask(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public  Task storeTask(AsnDetail d, PickDetail p, Area area, ItemKey ik, Point srcPoint, Point dstPoint,double qty,String stockCode) {
        Task t = new Task();
        t.setAsnDetail(d);
        t.setPickDetail(p);
        if(d!=null){
            if(d.getAsn()!=null){
                 t.setBillCode(d.getAsn().getCode());
            }
            t.setTaskType(BizStatus.ASN);
            t.setItem(d.getItem());
        }else if(p!=null){
            if(p.getPickTicket()!=null){
                t.setBillCode(p.getPickTicket().getCode());
            }
            t.setTaskType(BizStatus.PICK);
            t.setItem(p.getItem());
        }
        t.setPlanQty(qty);
        t.setTaskStatus(BizStatus.OPEN);
        t.setItemKey(ik);
        t.setArea(area);
        t.setDept(srcPoint.getDept());
        if (srcPoint != null) {
            t.setSrcPoint(srcPoint);
            t.setSrcPointCode(srcPoint.getCode());
        }
        if(dstPoint!=null){
            t.setDstPoint(dstPoint);
            t.setDstPointCode(dstPoint.getCode());
        }
        t.setDstStockCode(stockCode);
        this.create(t);
        return t;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void pickConfirm(long taskId, double pickedQuantity) throws Exception {
        if (taskId > 0) {
            TaskDto taskDto = findById(taskId);
            Task task = taskMapper.toEntity(taskDto);//Dto转实体
            if (task.getPlanQty() > task.getMoveQty()) {
                PickDetail pickDetail = task.getPickDetail();
                task.setMoveQty(pickedQuantity);
                task.setTaskStatus(BizStatus.FINISH);
                taskRepository.save(task);
                Inventory inv = inventoryRepository.findById(task.getInvId()).get();
                if (inv != null) {
                    ItemKey itemKey = inv.getItemKey();
                    Item item = itemKey.getItem();
                    Dept dept = item.getDept();
                    double srcQty=inv.getQuantity();
                    inv.setQuantity(inv.getQuantity() - pickedQuantity);
                    inv.setQueuedQty(inv.getQueuedQty() - pickedQuantity);
                    inventoryRepository.save(inv);
                    if(inv.getQuantity()<=0){
                        inventoryRepository.delete(inv);
                    }
                    Stock stock=inv.getStock();
                    stock.setStatus(BaseStatus.FREE);
                    stockRepository.save(stock);
                    //添加库存日志
                    inventoryLogService.storeInventoryLog(BizStatus.PICK_DOWN,BizStatus.REDUCE,task.getPickDetail().getPo(),null,task.getItemKey(),task.getSrcPoint(),task.getDstPoint(),task.getSrcStock(),task.getDstStock(),srcQty,pickedQuantity,
                            null,null,BizStatus.PICK,task.getId(),inv.getId(),"");

                    //刷新出库明细上的拣货数量
                    pickDetail.setPickedQty(pickDetail.getPickedQty() + pickedQuantity);
                    if (pickDetail.getOrderQty().equals(pickDetail.getPickedQty())) {
                        pickDetail.setStatus(BizStatus.PICK_ALL);
                    } else if (pickDetail.getAllocatedQty() > 0 && pickDetail.getOrderQty() - pickDetail.getPickedQty() > 0) {
                        pickDetail.setStatus(BizStatus.PICKUP);
                    }
                    pickDetailRepository.save(pickDetail);

                } else {
                    throw new RuntimeException("未找到库存相应记录！");
                }
            }

        } else {
            throw new RuntimeException("参数错误或为空");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void pickBarBack(long taskId, double pickedQuantity) throws Exception {
        if (taskId > 0) {
            TaskDto taskDto = findById(taskId);
            Task task = taskMapper.toEntity(taskDto);//Dto转实体
            PickDetail pickDetail = task.getPickDetail();
            Inventory inv = inventoryRepository.findById(task.getInvId()).get();
            if (inv != null) {
                //退回库存
                double srcQty=inv.getQueuedQty();
                inv.setQueuedQty(inv.getQueuedQty()+pickedQuantity);
                inv.setQuantity(inv.getQuantity()+pickedQuantity);
                inventoryRepository.save(inv);
                //添加库存日志
                inventoryLogService.storeInventoryLog(BizStatus.PICK_CANCEL,BizStatus.ADD,task.getPickDetail().getPo(),null,task.getItemKey(),task.getDstPoint(),task.getSrcPoint(),task.getSrcStock(),task.getDstStock(),srcQty,pickedQuantity,
                        null,null,BizStatus.PICK,task.getId(),inv.getId(),"");
                //退回Task
                task.setMoveQty(task.getMoveQty() - pickedQuantity);
                taskRepository.save(task);

                //退回出库明细
                pickDetail.setPickedQty(pickDetail.getPickedQty() - pickedQuantity);
                if (pickDetail.getPickedQty() == 0) {
                    pickDetail.setStatus(BizStatus.ALLOCATE);
                }
                pickDetailRepository.save(pickDetail);
            } else {
                throw new RuntimeException("未找到库存相应记录！");
            }

        } else {
            throw new RuntimeException("参数错误或为空");
        }
    }

    public  List<RPTaskList> queryTaskApp(IPTask t){
        String sql="SELECT t.pick_detail_id pickDetailId,sh.code gw, it.code itemCode,it.`name` itemName,t.plan_qty qty," +
                "ik.prop_c1 pch,t.move_qty bhs,t.src_point_code srcPointCode,t.src_stock_code stockCode,t.dst_point_code dstPointCode\n" +
                " from data_task t\n" +
                "left join data_pick_detail d on t.pick_detail_id=d.id\n" +
                "left join base_area sh on sh.id=d.sh_area_id\n" +
                "left join data_item_key ik on ik.id=t.item_key_id\n" +
                "left join base_item it on it.id=ik.item_id\n " +
                " where 1=1 ";
        if(t.getPickTicketId()!=null&&!t.getPickTicketId().equals(0l)){
            sql+=" and d.pick_ticket_id="+t.getPickTicketId();
        }else{
            sql+=" and  d.`status`='ALLOCATE'";
        }
        sql+= " ORDER BY it.code";
        List ts= entityMapper.createNativeQuery(sql)
                .unwrap(SQLQuery.class)
                .setResultTransformer(
                        AliasToEntityMapResultTransformer.INSTANCE
                )
                .list();
        List<RPTaskList> l = JSON.parseArray(JSON.toJSONString(ts),RPTaskList.class);
        return  l;
    }

    @Override
    public List<Task> getAgvTaskList(Integer id) {
        return taskRepository.getAgvTaskList(id);
    }

    @Override
    public List<Task> findbyPointCode(Long pointId) {
        String hql=" from Task t where t.dstPoint.id="+pointId+" and t.moveQty=0 ";
        Query query=entityMapper.createQuery(hql);
        List<Task> taskList=query.getResultList();
        return taskList;
    }

    public List<Object[]> queryAreaMonth(String type) {
        List<Object[]> taskList=null;
        String hql = "";
        if(type.equals("IN")){
            hql += " select t.dstPoint.area.name,count(t.id) from Task t where  t.dstPoint.area.name in ('"+AreaNameDic.QHTL+"','"+AreaNameDic.QHTK+"','"+AreaNameDic.ZCK+"','"+AreaNameDic.ZCM+"')";
            hql+=" and t.asnDetail.id is not null ";
            hql+=" group by  t.dstPoint.area.name";
        }else if(type.equals("OUT")){
            hql += " select t.srcPoint.area.name,count(t.id) from Task t where  t.srcPoint.area.name in ('"+AreaNameDic.QHTL+"','"+AreaNameDic.QHTK+"','"+AreaNameDic.ZCK+"','"+AreaNameDic.ZCM+"')";
            hql+=" and t.pickDetail.id is not null ";
            hql+=" group by  t.srcPoint.area.name";
        }
        Query query = entityMapper.createQuery(hql);
        taskList = query.getResultList();
        return taskList;
    }

    public List<Object[]> queryAreaDay(String type,String zoneType) {
        List<Object[]> taskList=null;
        String hql = "select count(t.id),CAST(sum(t.moveQty) as integer )from Task t where 1=1";
        if(type.equals("IN")){
            if(zoneType.equals("ZC")) {
                hql += " and t.dstPoint.area.name in ('" + AreaNameDic.ZCM + "')";
            }
            hql+=" and t.asnDetail.id is not null ";
        }else if(type.equals("OUT")){
            if(zoneType.equals("ZC")) {
                hql += " and t.srcPoint.area.name in ('" + AreaNameDic.ZCM + "')";
            }
            hql+=" and t.pickDetail.id is not null ";
        }
        hql += " and DATE_FORMAT(t.createTime,'%Y-%m-%d')=DATE_FORMAT(now(),'%Y-%m-%d')";
        Query query = entityMapper.createQuery(hql);
        taskList = query.getResultList();
        return taskList;
    }

    @Override
    public List<Task> queryTask(Long asnDetailId,Long pickDetailId,Long ikId,Long pickId,Long srcPointId) {
        String hql=" from Task t where 1=1 ";
        if(asnDetailId!=null){
            hql+=" and t.asnDetail.id="+asnDetailId+"  and  t.taskType='ASN'";
        }else if(pickDetailId!=null){
            hql+=" and  t.taskType='PICK' and t.pickDetail.id="+pickDetailId;
        }
        if(ikId!=null){
            hql+=" and t.itemKey.id="+ikId;
        }
        if(pickId!=null){
            hql+=" and t.pickDetail.pickTicket.id="+pickId;
        }
        if(srcPointId!=null){
            hql+=" and t.srcPoint.id="+srcPointId;
        }
        Query query=entityMapper.createQuery(hql);
        List<Task> taskList=query.getResultList();
        return taskList;
    }

}
