/*
 *  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.basicdata.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.youchain.Netty.NettyUtils;
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.service.ItemService;
import com.youchain.basicdata.service.PointService;
import com.youchain.basicdata.service.StockService;
import com.youchain.basicdata.service.dto.ItemDto;
import com.youchain.basicdata.service.dto.PointDto;
import com.youchain.businessdata.domain.*;
import com.youchain.businessdata.service.*;
import com.youchain.exception.handler.ApiResult;
import com.youchain.modules.system.repository.DictRepository;
import com.youchain.modules.system.service.DictService;
import com.youchain.modules.system.service.dto.DictDto;
import com.youchain.modules.system.service.dto.DictQueryCriteria;
import com.youchain.utils.*;
import lombok.RequiredArgsConstructor;
import com.youchain.basicdata.repository.BoxRepository;
import com.youchain.basicdata.service.BoxService;
import com.youchain.basicdata.service.dto.BoxDto;
import com.youchain.basicdata.service.dto.BoxQueryCriteria;
import com.youchain.basicdata.service.mapstruct.BoxMapper;
import lombok.extern.slf4j.Slf4j;
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.sql.Timestamp;
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-17
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class BoxServiceImpl implements BoxService {

    private final BoxRepository boxRepository;
    private final BoxMapper boxMapper;
    private final EntityManager entityManager;
    private final PointService pointService;
    private final StockService stockService;
    private final ItemService itemService;
    private final AsnDetailService asnDetailService;
    private final ItemKeyService itemKeyService;
    private final AgvTaskService agvTaskService;
    private final TaskService taskService;
    private final InventoryService inventoryService;
    private final PickDetailService pickDetailService;
    private final DictRepository dictRepository;
    private final RedisUtils redisUtils;
    private final DictService dictService;

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

    @Override
    public List<BoxDto> queryAll(BoxQueryCriteria criteria) {
        return boxMapper.toDto(boxRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    @Transactional
    public BoxDto findById(Long id) {
        Box box = boxRepository.findById(id).orElseGet(Box::new);
        ValidationUtil.isNull(box.getId(), "Box", "id", id);
        return boxMapper.toDto(box);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BoxDto create(Box resources) {
        return boxMapper.toDto(boxRepository.save(resources));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Box resources) {
        redisUtils.del("ButtonBoxCode");
        //判断那个按钮盒
        if (resources.getLampCode() != null && resources.getLampCode().length() > 0) {
            switch (resources.getLampCode()) {
                case "0001":
                    this.executeLampStatus(resources.getLampStatus(), resources.getIP(), NettyUtils.light_1);
                    break;
                case "0002":
                    this.executeLampStatus(resources.getLampStatus(), resources.getIP(), NettyUtils.light_2);
                    break;
                case "0003":
                    this.executeLampStatus(resources.getLampStatus(), resources.getIP(), NettyUtils.light_3);
                    break;
                case "0004":
                    this.executeLampStatus(resources.getLampStatus(), resources.getIP(), NettyUtils.light_4);
                    break;
                default:
                    log.error("{}:按钮盒编码【{}】出错", resources.getCode(), resources.getLampCode());
                    return;
            }
        }

        //根据IP和按钮盒编号查询是否存在了，存在提示，不存在绑定
        if ((resources.getIP() != null && resources.getIP().length() > 0) && (resources.getLampCode() != null && resources.getLampCode().length() > 0)) {
            String hql = " from Box box where box.iP='" + resources.getIP() + "' and box.lampCode='" + resources.getLampCode() + "' and box.id!=" + resources.getId() + " ";
            Query query = entityManager.createQuery(hql);
            List<Box> boxList = query.getResultList();
            if (boxList.size() > 0) {
                StringBuffer sbf = new StringBuffer();
                for (Box box : boxList) {
                    sbf.append(box.getLampCode()).append(",").toString();
                }
                throw new RuntimeException("按钮盒编号" + sbf.deleteCharAt(sbf.length() - 1) + "已绑定!");
            }
        }
        Box box = boxRepository.findById(resources.getId()).orElseGet(Box::new);
        ValidationUtil.isNull(box.getId(), "Box", "id", resources.getId());
        box.copy(resources);
        boxRepository.save(box);


    }

    /**
     * 亮对应的灯
     *
     * @param lamp_status 指示灯状态 ->灯光熄灭 light_off、灯光常亮 light_on、灯光快闪 light_on_fast_flicker、
     *                    灯光慢闪 light_on_slow_flicker、按钮释放 button_off
     * @param ip          ip地址
     * @param index       对应的盒子
     * @return
     */
    public Boolean executeLampStatus(String lamp_status, String ip, String index) {
        switch (lamp_status) {
            case "0":
                NettyUtils.light_off(ip, index, null);
                break;
            case "1":
                NettyUtils.light_on(ip, index, null);
                break;
            case "2":
                NettyUtils.light_on_fast_flicker(ip, index, null);
                break;
            case "3":
                NettyUtils.light_on_slow_flicker(ip, index, null);
                break;
            default:
                log.error("按钮盒状态修改错误:0~3");
                return false;
        }
        return true;
    }

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

    @Override
    public void download(List<BoxDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (BoxDto box : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("物料ID", box.getItem());
            map.put("点位ID", box.getPoint());
//            map.put("IP", box.getIP());
//            map.put("指示灯状态", box.getLampStatus());
//            map.put("按钮盒编号",box.getLampCode());
            map.put("创建人", box.getCreateBy());
            map.put("修改人", box.getUpdateBy());
            map.put("创建时间", box.getCreateTime());
            map.put("修改时间", box.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public Box getBoxByItem(long itemId) {
        return boxRepository.getBoxByItem(itemId);
    }

    @Override
    public Box getBoxByCode(String code) {
        return boxRepository.getBoxByCode(code);
    }

    @Override
    public Box toEntity(BoxDto boxDto) {
        return boxMapper.toEntity(boxDto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refreshPointStatus() {
        String sql = "update Point point set point.status='USED'  where point.id in (select box.point.id from Box box) and point.type='SHIP' ";
        Query query = entityManager.createQuery(sql);
        long num = query.executeUpdate();

        String sql2 = "update Point point set point.status='FREE'  where point.id not in (select box.point.id from Box box) and point.type='SHIP' ";
        Query query2 = entityManager.createQuery(sql2);
        long num2 = query2.executeUpdate();

    }

    @Override
    public Box getBoxByPointCode(String pointCode) {
        Box box = null;
        String hql = " from Box box where box.point.code='" + pointCode + "'";
        Query query = entityManager.createQuery(hql);
        List<Box> BoxList = query.getResultList();
        if (BoxList.size() > 0) {
            box = BoxList.get(0);
            return box;
        }
        return box;
    }

    @Override
    public List<Box> queryBoxList(String areaName) {
        if (areaName.equals("前后桶部装")) {
            areaName = "前桶部装";
        }
        String hql = " from Box box where  box.enabled=true and box.point.area.name like '" + areaName + "%' order by box.sort ";
        Query query = entityManager.createQuery(hql);
        List<Box> boxList = query.getResultList();
        return boxList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void callBox(BoxDto boxDto) throws RuntimeException {
        String code = "0";
        String message = null;
        String agv_on_off = "OFF";
        agv_on_off = dictService.getDictDescription("agv_on_off") == null ? "OFF" : dictService.getDictDescription("agv_on_off").getDescription();
        if (boxDto.getPoint() == null) {
            throw new RuntimeException(boxDto.getCode() + "没维护点位");
        }
        String areaName = boxDto.getPoint().getArea().getName().trim();
        //呼叫空车
        if (areaName.equals(AreaNameDic.QTYK) || areaName.equals(AreaNameDic.HTYK)) {
            PointDto endPointDto = boxDto.getPoint();//目标点位
            Point endPoint = pointService.toEntity(endPointDto);//转实体
            if (endPoint.getStatus().equals(BaseStatus.FREE)) {
                //查询空料车
                Stock stock = null;
                String endAreaName = "";
                stock = stockService.findByKStock(areaName);
                if (stock == null) {
                    throw new RuntimeException(endAreaName + "空车区没有空料车!");
                }
                Point startPoint = stock.getPoint();
                //生成搬运任务
                AgvTask agvTask = new AgvTask(BizStatus.ASN, stock.getCode(), startPoint.getCode(), endPointDto.getCode(), BizStatus.OPEN, "RACK_MOVE_EMPTY");
                agvTaskService.create(agvTask);
                if (agv_on_off.equals("ON")) {
                    String resultJson = agvTaskService.sendAgvTaskImpl(agvTask);//发送任务
                    JSONObject resulObject = JSON.parseObject(resultJson);
                    code = resulObject.getString("code") == null ? "" : resulObject.getString("code");
                    message = resulObject.getString("message") == null ? "" : resulObject.getString("message");
                }
                if (code.equals("0")) {
                    //按钮盒点位占用
                    endPoint.setStatus(BaseStatus.USED);
                    pointService.update(endPoint);
                    if (stock != null) {
                        stock.setPoint(endPoint);
                        stockService.update(stock);
                    }
                } else {
                    throw new RuntimeException(message + "AGV返回消息");
                }
            } else {
                throw new RuntimeException(endPoint.getCode() + "点位已有任务，请勿重复操作!");
            }
        } else if (areaName.equals("前桶预装满车区") || areaName.equals("后桶预装满车区")) {//满车入库
            PointDto stratPointDto = boxDto.getPoint();//目标点位
            Point stratPoint = pointService.toEntity(stratPointDto);//转实体
            ItemDto itemDto = boxDto.getItem();//物料
            Item item = itemService.toEntity(itemDto);//转实体
            if (stratPoint.getStatus().equals(BaseStatus.FREE)) {
                if (item.getGoodType() == null || item.getGoodType().length() <= 0) {
                    throw new RuntimeException("物料没有维护类型！");
                }

                Point endPoint = pointService.getPointByFreeQH("前后桶冷却区", item.getGoodType());
                if (endPoint == null) {
                    throw new RuntimeException(item.getGoodType()+"存储类型没有空闲点位，请调整冷却区-小区库!");
                }

                //生成搬运任务
                if (agvTaskService.findByendSlotCode(endPoint.getCode(), BizStatus.ASN, "RACK_MOVE")) {
                    AgvTask agvTask = new AgvTask(BizStatus.ASN, null, stratPoint.getCode(), endPoint.getCode(), BizStatus.OPEN, "RACK_MOVE_FULL");
                    agvTaskService.create(agvTask);
                    if (agv_on_off.equals("ON")) {
                        String resultJson = agvTaskService.sendAgvTaskImpl(agvTask);//发送任务
                        JSONObject resulObject = JSON.parseObject(resultJson);
                        code = resulObject.getString("code") == null ? "" : resulObject.getString("code");
                        message = resulObject.getString("message") == null ? "" : resulObject.getString("message");
                    }
                    if (code.equals("0")) {
                        /*//按钮盒点位占用,空车缓存点位占用
                        stratPoint.setStatus(BaseStatus.USED);
                        pointService.update(stratPoint);
                        endPoint.setStatus(BaseStatus.USED);
                        pointService.update(endPoint);
                        //生成入库任务
                        Date date = cn.hutool.core.date.DateUtil.date();
                        String propC1 = DateUtil.format(date, "yyyyMMdd");
                        AsnDetail asnDetail = asnDetailService.createAsnDetail(item, null, stratPoint, propC1, boxDto.getOrderNumber());
                        asnDetailService.create(asnDetail);
                        //生成Itemkey
                        ItemKey itemKey = itemKeyService.getItemKey(item, asnDetail.getPropC1(), asnDetail.getOrderNumber());
                        //生成入库记录
                        Task task = new Task(item, itemKey, asnDetail.getOrderNumber(), BizStatus.ASN, asnDetail, null, null, null, null, null, stratPoint, endPoint, null, null, stratPoint.getCode(), endPoint.getCode(), null, BizStatus.ATCALL, asnDetail.getOrderQty(), null, null, item.getDept(), agvTask);
                        taskService.create(task);*/
                    } else {
                        throw new RuntimeException(message);
                    }
                } else {
                    throw new RuntimeException(endPoint.getCode() + "点位已有任务，请勿重复操作!");
                }

            } else {
                throw new RuntimeException(stratPoint.getCode() + "点位已有任务，请勿重复操作!");
            }
        } else if (areaName.equals("前桶部装空车区")
                || areaName.equals("后桶部装空车区")
                || areaName.equals(AreaNameDic.MFBZK)) {//空车入库
            //空车回库；前后桶空车区、前后桶部装空车缓存区
            PointDto startPointDto = boxDto.getPoint();//起始点位
            Point startPoint = pointService.toEntity(startPointDto);//转实体
            if (startPoint.getStatus().equals(BaseStatus.FREE)) {
                Point endPoint = null;
                if (areaName.equals(AreaNameDic.MFBZK)) {
                    endPoint = pointService.getPointByFree(areaName, null);
                } else {
                    endPoint = pointService.getPointByFree("前后桶部装空车缓存区,前后桶空车区", null);
                }
                if (endPoint == null) {
                    throw new RuntimeException("空车区没有空闲点位!");
                }
                //生成搬运任务
                AgvTask agvTask = new AgvTask(BizStatus.PICK, null, startPoint.getCode(), endPoint.getCode(), BizStatus.OPEN, "RACK_MOVE_EMPTY");
                agvTaskService.create(agvTask);
                if (agv_on_off.equals("ON")) {
                    String resultJson = agvTaskService.sendAgvTaskImpl(agvTask);//发送任务
                    JSONObject resulObject = JSON.parseObject(resultJson);
                    code = resulObject.getString("code") == null ? "" : resulObject.getString("code");
                    message = resulObject.getString("message") == null ? "" : resulObject.getString("message");
                }
                if (code.equals("0")) {
                    //空车缓存点位占用，按钮盒点位占用
                    endPoint.setStatus(BaseStatus.USED);
                    pointService.update(endPoint);
                    startPoint.setStatus(BaseStatus.USED);
                    pointService.update(startPoint);
                } else {
                    throw new RuntimeException(message);
                }

            } else {
                throw new RuntimeException(startPoint.getCode() + "点位已有任务，请勿重复操作!");
            }
        } else if (boxDto.getPoint().getArea().getName().equals("前桶部装满车区")
                || boxDto.getPoint().getArea().getName().equals("后桶部装满车区")
                || boxDto.getPoint().getArea().getName().equals(AreaNameDic.MFBZM)) {//呼叫满车
            PointDto endPointDto = boxDto.getPoint();
            Point endPoint = pointService.toEntity(endPointDto);
            ItemDto itemDto = boxDto.getItem();
            Item item = itemService.toEntity(itemDto);
            if (endPoint.getStatus().equals(BaseStatus.FREE)) {
                //部装走出库流程-生成出库任务
                List<Inventory> inventoryList = inventoryService.queryInventory(item.getId());
                if (inventoryList.size() == 0) {
                    throw new RuntimeException("无库存,呼叫失败!");
                }
                PickDetail pickDetail = pickDetailService.createPickDetail(item, boxDto.getOrderNumber());
                try {
                    pickDetailService.allocateAll(pickDetail.getId(), endPoint);
                } catch (Exception e) {
                    throw new RuntimeException(e.getMessage());
                }
                List<Task> taskList = taskService.getPickNotAllTask(pickDetail.getId());
                AgvTask agvTask = taskList.get(0).getAgvTask();
                if (agv_on_off.equals("ON")) {
                    String resultJson = agvTaskService.sendAgvTaskImpl(agvTask);//发送任务
                    JSONObject resulObject = JSON.parseObject(resultJson);
                    code = resulObject.getString("code") == null ? "" : resulObject.getString("code");
                    message = resulObject.getString("message") == null ? "" : resulObject.getString("message");
                }
                if (code.equals("0")) {
                    //按钮盒点位占用
                    endPoint.setStatus(BaseStatus.USED);
                    pointService.update(endPoint);
                } else {
                    throw new RuntimeException(message);
                }

            } else {
                throw new RuntimeException(endPoint.getCode() + "点位已有任务，请勿重复操作!");
            }
        }
    }

    @Override
    public void callTrolley(PointDto pointDto, String pointCode) {
        //判断判断呼叫点位
        if (pointDto.getArea().getName().equals(AreaNameDic.ZCFB)) {
            //pointDto 为目标点位
            Point endPoint = pointService.toEntity(pointDto);//轴承座入库缓存区转实体
            if (endPoint.getStatus().equals(BaseStatus.FREE)) {//轴承座入库缓存区要为空
                //轴承座空车区 是占用 名字是：轴承座空车区
//                Point startPoint = pointService.findByCode(null, BaseStatus.USED, BaseStatus.STORAGE, "轴承座空车区", null);
                Point startPoint = pointService.findByCode(null, BaseStatus.USED, BaseStatus.STORAGE, AreaNameDic.ZCK, null);
                if (startPoint == null) {
                    throw new RuntimeException("空车区没有空料车!");
                }
                //查询料车
                Stock stock = stockService.findByPointCode(startPoint.getCode());
                if (stock == null) {
                    throw new RuntimeException(startPoint.getCode() + "没有绑定容器!");
                }
                //生成搬运任务
                AgvTask agvTask = new AgvTask(BizStatus.ASN, stock.getCode(), startPoint.getCode(), pointDto.getCode(), BizStatus.OPEN, "RACK_MOVE");
                agvTaskService.create(agvTask);
                //下面注释掉
//                String resultJson = agvTaskService.sendAgvTaskImpl(agvTask);//发送任务
//                JSONObject resulObject = JSON.parseObject(resultJson);
//                String code = resulObject.getString("code") == null ? "" : resulObject.getString("code");
//                String message = resulObject.getString("message") == null ? "" : resulObject.getString("message");
//                @org.springframework.data.jpa.repository.Query(value = "SELECT * FROM sys_dict b WHERE b.name = ?1 ", nativeQuery = true)
//                String getDictDescription(String name);
                String code = dictRepository.getDictDescription("agv_on_off").getDescription();
                endPoint.setStatus(BaseStatus.USED);
                pointService.update(endPoint);
                startPoint.setStatus(BaseStatus.FREE);
                pointService.update(startPoint);
                if (code.equals("ON")) {//根据字典进行判断执行与否
                    //按钮盒点位占用,空车缓存点位释放
//                    endPoint.setStatus(BaseStatus.USED);
//                    pointService.update(endPoint);
//                    startPoint.setStatus(BaseStatus.FREE);
//                    pointService.update(startPoint);
                } else {
                    throw new RuntimeException("任务生产失败");
                }
            } else {
                throw new RuntimeException(endPoint.getCode() + "点位已有任务，请勿重复操作!");
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Task createTask(Item item, Stock stock, Point endPoint) {
        AsnDetail asnDetail = new AsnDetail();
        asnDetail.setItem(item);
        asnDetail.setLineNo(1l);
        asnDetail.setStatus(BizStatus.OPEN);
        asnDetail.setOrderQty(item.getExtendD1());
        asnDetail.setDept(item.getDept());
        Date date = cn.hutool.core.date.DateUtil.date();
        String propC1 = DateUtil.format(date, "yyyyMMdd");
        asnDetail.setPropC1(propC1);
        asnDetailService.create(asnDetail);

        //生成Itemkey
        ItemKey itemKey = itemKeyService.getItemKey(item, asnDetail.getPropC1(), asnDetail.getOrderNumber());


        //生成入库记录
        Task task = new Task(item, itemKey, asnDetail.getOrderNumber(), BizStatus.ASN, asnDetail, null, null, null, stock, stock, null, endPoint, null, null, null, endPoint.getCode(), null, BizStatus.OPEN, asnDetail.getOrderQty(), null, null, item.getDept(), null);
        taskService.create(task);
        return task;
    }

}
