package org.springblade.modules.api.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.aspect.VendingFlag;
import org.springblade.common.constant.CommonConstant;
import org.springblade.common.enums.FacilityStatusEnum;
import org.springblade.common.enums.OrderStateEnum;
import org.springblade.core.log.annotation.ApiLog;
import org.springblade.modules.api.request.VendingHeartReq;
import org.springblade.modules.api.request.VendingOrderReq;
import org.springblade.modules.api.request.VendingShipReq;
import org.springblade.modules.api.response.VendingOrderGoodsRes;
import org.springblade.modules.api.response.VendingOrderRes;
import org.springblade.modules.api.service.VendingOrderService;
import org.springblade.modules.business.entity.Facility;
import org.springblade.modules.business.entity.FacilityFault;
import org.springblade.modules.business.enums.FacilityFaultStatusEnum;
import org.springblade.modules.business.service.IFacilityFaultService;
import org.springblade.modules.business.service.IFacilityService;
import org.springblade.modules.finance.entity.Order;
import org.springblade.modules.finance.entity.OrderGoods;
import org.springblade.modules.finance.service.IOrderGoodsService;
import org.springblade.modules.finance.service.IOrderService;
import org.springblade.modules.finance.vo.OrderGoodsVO;
import org.springblade.modules.finance.vo.OrderVO;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 售货机硬件对接接口
 */
@RestController
@AllArgsConstructor
@RequestMapping(CommonConstant.API_URL + "/vending")
@Api(value = "售货机硬件对接接口", tags = "售货机硬件对接接口")
@Slf4j
public class ApiVendingController {

    private final VendingOrderService vendingOrderService;
    private final IOrderGoodsService orderGoodsService;
    private final IOrderService orderService;
    private final IFacilityService facilityService;
    private final IFacilityFaultService facilityFaultService;


    @ApiLog("查询设备订单信息")
    @VendingFlag
    @ApiOperation(value = "查询订单信息", notes = "传入售货机编号")
    @PostMapping("/getOrder")
    public void getOrder(@Valid @RequestBody VendingOrderReq vendingOrderReq, HttpServletResponse response) throws IOException {
        List<OrderGoodsVO> vendingOrderVOs = vendingOrderService.getVendingOrder(vendingOrderReq);

        //封装返回
        VendingOrderRes vendingOrderRes = new VendingOrderRes();
        vendingOrderRes.setMachineId(vendingOrderReq.getMachineId());
        if (CollectionUtil.isEmpty(vendingOrderVOs)) {
            vendingOrderRes.setSaleSn("");
            vendingOrderRes.setChannelNum("0");
            vendingOrderRes.setSaleData(new ArrayList<>());
            this.notifyReturn(response, vendingOrderRes);
        } else {
            //一次只返回一个订单
            //按订单分组
            Map<Long, List<OrderGoodsVO>> vendingOrderGoodMap = vendingOrderVOs.stream().collect(Collectors.groupingBy(OrderGoodsVO::getOrderId));
            OrderVO backOrder = vendingOrderService.getBackOrder(vendingOrderReq);
            if (Objects.isNull(backOrder)) {
                vendingOrderRes.setSaleSn("");
                vendingOrderRes.setChannelNum("0");
                vendingOrderRes.setSaleData(new ArrayList<>());
                this.notifyReturn(response, vendingOrderRes);
                return;
            }
            List<OrderGoodsVO> orderGoodsVOS = vendingOrderGoodMap.get(backOrder.getId());
            vendingOrderRes.setSaleSn(backOrder.getId() + "");
            vendingOrderRes.setChannelNum(orderGoodsVOS.size() + "");

            List<VendingOrderGoodsRes> saledataList = new ArrayList<>();

            //补充货道信息
            for (OrderGoodsVO orderGoodsVO : orderGoodsVOS) {
                VendingOrderGoodsRes vendingOrderGoodsRes = new VendingOrderGoodsRes();
                vendingOrderGoodsRes.setOrderGoodsId(String.valueOf(orderGoodsVO.getId()));
                vendingOrderGoodsRes.setChannel(orderGoodsVO.getCargoWayNo());
                vendingOrderGoodsRes.setChannelNum(String.valueOf(orderGoodsVO.getGoodsNum()));

                saledataList.add(vendingOrderGoodsRes);
            }

            vendingOrderRes.setSaleData(saledataList);
            this.notifyReturn(response, vendingOrderRes);
        }

    }

    /**
     * 硬件接口订单信息返回通用响应
     *
     * @param response        HttpServletResponse
     * @param vendingOrderRes 返值
     */
    private void notifyReturn(HttpServletResponse response, VendingOrderRes vendingOrderRes) throws IOException {
        Map<String, Object> map = BeanUtil.beanToMap(vendingOrderRes);
        response.setHeader("Content-type", ContentType.JSON.toString());
        response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
        response.flushBuffer();
    }

    @ApiLog("设备出货成功回调")
    @ApiOperation(value = "出货成功回调", notes = "出货成功回调")
    @PostMapping("callback")
    public void callback(@Valid @RequestBody VendingShipReq req) {
        //设备出货成功回调
        OrderGoods orderGoods = orderGoodsService.getById(req.getOrderGoodsId());

        if (Objects.isNull(orderGoods)) {
            log.info("设备出货成功回调，未找到货品信息");
            return;
        }

        //出货量校验
        if (orderGoods.getGoodsNum() < orderGoods.getShipmentNum() + 1) {
            log.info("设备出货成功回调，出货量过多");
        }

        if ("OK".equals(req.getStatus())) {
            orderGoodsService.lambdaUpdate()
                    .set(OrderGoods::getShipmentNum, orderGoods.getShipmentNum() + 1)
                    .set(OrderGoods::getShippingTime, LocalDateTime.now())
                    .eq(OrderGoods::getId, req.getOrderGoodsId())
                    .update();

            if (orderGoods.getGoodsNum() == orderGoods.getShipmentNum() + 1) {
                orderService.lambdaUpdate()
                        .set(Order::getOrderState, OrderStateEnum.COMPLETED.getValue())
                        .eq(Order::getId, req.getSaleSn())
                        .update();

                orderGoodsService.lambdaUpdate()
                        .set(OrderGoods::getShipment, 1)
                        .eq(OrderGoods::getId, req.getOrderGoodsId())
                        .update();
            }
        } else {//出货故障处理
            //将订单状态改为出货失败
            orderService.lambdaUpdate()
                    .set(Order::getOrderState, OrderStateEnum.SHIP_FAIL.getValue())
                    .eq(Order::getId, req.getSaleSn())
                    .update();

            orderGoodsService.lambdaUpdate()
                    .set(OrderGoods::getShipment, 0)
                    .eq(OrderGoods::getId, req.getOrderGoodsId())
                    .update();
        }

    }

    @ApiLog("设备心跳")
    @ApiOperation(value = "设备心跳", notes = "设备心跳")
    @PostMapping("heartbeat")
    public void heartbeat(@Valid @RequestBody VendingHeartReq req) {
        Facility facility = facilityService.getById(req.getMachineId());
        if (Objects.isNull(facility)) {
            log.info("设备心跳，未找到设备信息");
            return;
        }
        Date now = new Date();
        facility.setHeartbeatTime(now);
        if ("0".equals(req.getErrorNum()) || "OK".equals(req.getStatus())) {
            facility.setFacilityStatus(FacilityStatusEnum.ON_LINE.getValue());
            facilityService.updateById(facility);

            FacilityFault facilityFault = new FacilityFault();
            facilityFault.setFacilityId(req.getMachineId());
            facilityFault.setFaultDesc(FacilityFaultStatusEnum.getDesc("0"));
            facilityFault.setFaultStatus("0");
            facilityFault.setCreateTime(now);
            facilityFaultService.save(facilityFault);
            return;
        } else {
            //设备故障
            facility.setFacilityStatus(FacilityStatusEnum.FAULT.getValue());
            facilityService.updateById(facility);

            //录入故障信息
            String[] split = req.getErrorArray().replace("{", "").replace("}", "").split(",");

            List<FacilityFault> facilityFaults = new ArrayList<>();
            for (String status : split) {
                FacilityFault facilityFault = new FacilityFault();
                facilityFault.setFacilityId(req.getMachineId());
                facilityFault.setFaultDesc(FacilityFaultStatusEnum.getDesc(status));
                facilityFault.setFaultStatus(status);
                facilityFault.setCreateTime(now);
                facilityFaults.add(facilityFault);
            }
            facilityFaultService.saveBatch(facilityFaults);
        }
    }

}
