/*
 *  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.Point;
import com.youchain.basicdata.domain.Stock;
import com.youchain.basicdata.repository.StockRepository;
import com.youchain.basicdata.service.PointService;
import com.youchain.businessdata.domain.AgvTask;
import com.youchain.businessdata.domain.AsnDetail;
import com.youchain.businessdata.domain.Inventory;
import com.youchain.businessdata.domain.Task;
import com.youchain.businessdata.repository.TaskRepository;
import com.youchain.businessdata.service.AsnDetailService;
import com.youchain.businessdata.service.TaskService;
import com.youchain.exception.BadRequestException;
import com.youchain.utils.*;
import lombok.RequiredArgsConstructor;
import com.youchain.businessdata.repository.AgvTaskRepository;
import com.youchain.businessdata.service.AgvTaskService;
import com.youchain.businessdata.service.dto.AgvTaskDto;
import com.youchain.businessdata.service.dto.AgvTaskQueryCriteria;
import com.youchain.businessdata.service.mapstruct.AgvTaskMapper;
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.util.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;

/**
 * @author huojin
 * @website https://eladmin.vip
 * @description 服务实现
 * @date 2024-09-25
 **/
@Service
@RequiredArgsConstructor
public class AgvTaskServiceImpl implements AgvTaskService {

    private final AgvTaskRepository agvTaskRepository;
    private final StockRepository stockRepository;
    private final TaskRepository taskRepository;
    private final AgvTaskMapper agvTaskMapper;
    private final PointService pointService;
    private final TaskService taskService;
    private final AsnDetailService asnDetailService;

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

    @Override
    public List<AgvTaskDto> queryAll(AgvTaskQueryCriteria criteria) {
        return agvTaskMapper.toDto(agvTaskRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    @Transactional
    public AgvTaskDto findById(Long id) {
        AgvTask agvTask = agvTaskRepository.findById(id).orElseGet(AgvTask::new);
        ValidationUtil.isNull(agvTask.getId(), "AgvTask", "id", id);
        return agvTaskMapper.toDto(agvTask);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AgvTaskDto create(AgvTask resources) {
        return agvTaskMapper.toDto(agvTaskRepository.save(resources));
    }

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

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

    @Override
    public void download(List<AgvTaskDto> all, HttpServletResponse response) throws Exception {
        List<Map<String, Object>> list = new ArrayList<>();
        for (AgvTaskDto agvTask : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("容器码", agvTask.getStockCode());
            map.put("容器类型", agvTask.getStockTypeCode());
            map.put("起点位置", agvTask.getStartSlotCode());
            map.put("终点位置", agvTask.getEndSlotCode());
            map.put("容器当前位置", agvTask.getSlotCode());
            map.put("任务状态", agvTask.getStatus());
            map.put("任务类型", agvTask.getType());
            map.put("下游任务号", agvTask.getJobId());
            map.put("工作类型", agvTask.getJobType());
            map.put("优先级", agvTask.getJobPriority());
            map.put("强制优先级", agvTask.getJobPriorityType());
            map.put("任务组", agvTask.getJobForce());
            map.put("任务组执行顺序", agvTask.getJobForceAsc());
            map.put("返回报文", agvTask.getJobMessage());
            map.put("变动时间", agvTask.getJobMessageTime());
            map.put("创建时间", agvTask.getCreateTime());
            map.put("开始时间", agvTask.getStartTime());
            map.put("完成时间", agvTask.getEndTime());
            map.put("创建人", agvTask.getCreateBy());
            map.put("修改人", agvTask.getUpdateBy());
            map.put("修改时间", agvTask.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public AgvTask toEntity(AgvTaskDto agvTaskDto) {
        return agvTaskMapper.toEntity(agvTaskDto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void agvTaskCallback(AgvTask agvTask, String status, String containerCode) {
        switch (status) {
            case "UP_CONTAINER":
            case "FORK_UP":
                handleUpContainer(agvTask, containerCode);//容器顶升
                break;
            case "COMPLETED":
                handleComContainer(agvTask, containerCode);//搬运任务完成
                break;
            case "CANCELED":
                handleCanceledTask(agvTask, containerCode);//容器取消
                break;
            case "RESEND":
                handleResendTask(agvTask);//重新发送
                break;
        }
    }


    private void handleUpContainer(AgvTask agvTask, String containerCode) {
        //顶升释放起点位置状态及容器绑定的起点位置
        Point startPoint = pointService.getPoint(agvTask.getStartSlotCode(), null, null, null);
        startPoint.setStatus(BaseStatus.FREE);
        pointService.update(startPoint);

        agvTask.setStockCode(containerCode);
        agvTask.setStatus(BizStatus.UP_CONTAINER);
        agvTask.setStartTime(new Timestamp(new Date().getTime()));
        agvTaskRepository.save(agvTask);
    }

    private void handleComContainer(AgvTask agvTask, String containerCode) {
        //任务完成；根据AGV任务的目标点位走对应流程
        Point endPoint = pointService.getPoint(agvTask.getEndSlotCode(), null, null, null);
        String endAreaName = endPoint.getArea().getCode();
        Stock stock = stockRepository.getStockByCode(containerCode);
        if (stock == null) {
            throw new BadRequestException(containerCode + "容器不存在! 请维护");
        }
        List<Task> taskList = taskService.getAgvTaskList(agvTask.getId());
        switch (endAreaName) {
            case "3#料箱区":
                handleXJDJ(agvTask, taskList, stock);//入库完成
                break;
        }

        agvTask.setStatus(BizStatus.FINISH);
        agvTask.setStockCode(containerCode);
        agvTask.setEndTime(new Timestamp(new Date().getTime()));
        agvTaskRepository.save(agvTask);
    }


    /**
     * 满车入库
     *
     * @param taskList
     * @param stock
     */
    private void handleXJDJ(AgvTask agvTask, List<Task> taskList, Stock stock) {
        if (BizStatus.ASN.equals(agvTask.getType())) {
            //容器回存储区，新增库存、更新点位状态
            for (Task task : taskList) {
                task.setTaskStatus(BizStatus.FINISH);
                task.setMoveQty(task.getPlanQty());
                taskService.update(task);
                //更新库存
            }
        }

    }

    /**
     * 任务取消完成
     *
     * @param agvTask
     * @param containerCode
     */
    private void handleCanceledTask(AgvTask agvTask, String containerCode) {
        //任务取消完成
        Point endPoint = pointService.getPoint(agvTask.getEndSlotCode(), null, null, null);
        String endAreaName = endPoint.getArea().getCode();
        Stock stock = stockRepository.getStockByCode(containerCode);
        if (stock == null) {
            throw new BadRequestException(containerCode + "容器不存在! 请维护");
        }
        switch (endAreaName) {
            case "3#料箱区":
                handleFBFCCancel(agvTask, endPoint, stock);//入库取消
                break;
        }
        agvTask.setStatus(BizStatus.CANCEL);
        agvTask.setEndTime(new Timestamp(new Date().getTime()));
        agvTaskRepository.save(agvTask);

    }

    private synchronized void handleResendTask(AgvTask agvTask) {
        List<AgvTask> repeatList = agvTaskRepository.findRepeat(agvTask.getStockCode());
        if (!repeatList.isEmpty()) {
            throw new BadRequestException("任务已重新生成，请勿重复操作! ");
        }
        AgvTask newAgvTask = new AgvTask();
        newAgvTask.setStockCode(agvTask.getStockCode());
        newAgvTask.setStartSlotCode(agvTask.getStartSlotCode());
        newAgvTask.setEndSlotCode(agvTask.getEndSlotCode());
        newAgvTask.setSlotCode(agvTask.getSlotCode());
        newAgvTask.setStatus(BizStatus.OPEN);
        newAgvTask.setType(agvTask.getType());
        newAgvTask.setJobType(agvTask.getJobType());
        newAgvTask.setJobPriority(1);
        newAgvTask.setLineSlotCode(agvTask.getLineSlotCode());
        agvTaskRepository.save(newAgvTask);

        //重新发送任务
        List<Task> taskList = taskService.getAgvTaskList(agvTask.getId());
        for (Task task : taskList) {
            task.setAgvTask(newAgvTask);
            taskRepository.save(task);
        }
    }

    private void handleFBFCCancel(AgvTask agvTask, Point endPoint, Stock stock) {
        //回存储区取消任务，不生成库存 入库任务取消即可
        List<Task> taskList = taskService.getAgvTaskList(agvTask.getId());
        for (Task task : taskList) {
            AsnDetail ad = task.getAsnDetail();
            ad.setStatus(BizStatus.CANCEL);
            asnDetailService.update(ad);

            task.setTaskStatus(BizStatus.CANCEL);
            taskService.update(task);
        }
        //起点点位释放
        Point srcPoint = pointService.getPoint(agvTask.getStartSlotCode(), null, null, null);
        releasePoint(srcPoint);
        stock.setStatus(BaseStatus.FREE);
        stockRepository.save(stock);
        //线边点位释放
        releasePoint(endPoint);
    }

    /**
     * 释放点位
     *
     * @param point
     */
    private void releasePoint(Point point) {
        if (point != null) {
            point.setStatus(BaseStatus.FREE);
            pointService.update(point);
        }
    }

}
