package com.fjhx.listener;

import cn.hutool.core.convert.Convert;
import cn.hutool.cron.CronUtil;
import com.alibaba.fastjson.JSONObject;
import com.fjhx.entity.MessageEntity;
import com.fjhx.event.WebSocketOnCloseEvent;
import com.fjhx.event.WebSocketOnMessageEvent;
import com.fjhx.event.WebSocketOnOpenEvent;
import com.fjhx.material.service.MaterialService;
import com.fjhx.service.WebSocketServer;
import com.fjhx.stock.service.StockCheckrecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class WebSocketEventListener {

    @Autowired
    private MaterialService materialService;

    @Autowired
    private StockCheckrecordService stockCheckrecordService;

    /**
     * 上位机userId
     */
    public static final String WEB_STOCK_PROGRAM = "webStockProgram";

    /**
     * 转发userId key
     */
    public static final String FORWARD_USER_ID = "forwardUserId";

    /**
     * 转发sessionId key
     */
    public static final String FORWARD_SESSION_ID = "forwardSessionId";

    /**
     * 初始化返回
     */
    public static final int ON_OPEN_TYPE = 1;

    /**
     * 操作上位机
     */
    public static final int OPERATING_UPPER_COMPUTER = 2;

    /**
     * 推送rfid
     */
    public static final int PUSH_RFID = 3;


    private final Map<String, MessageEntity> map = new ConcurrentHashMap<>();

    @EventListener
    public void onMessageListener(WebSocketOnOpenEvent event) {
        WebSocketServer source = event.getSource();
        JSONObject jsonObject = new JSONObject();
        source.sendMessage(ON_OPEN_TYPE, jsonObject);
    }

    @EventListener
    public void onMessageListener(WebSocketOnMessageEvent event) {
        WebSocketServer webSocketServer = event.getSource();
        String message = event.getMessage();
        MessageEntity sendEntity = JSONObject.parseObject(message, MessageEntity.class);

        Integer type = sendEntity.getType();
        JSONObject data = sendEntity.getData();

        switch (type) {
            case OPERATING_UPPER_COMPUTER:
                data.put(FORWARD_USER_ID, webSocketServer.getUserId());
                data.put(FORWARD_SESSION_ID, webSocketServer.getSessionId());
                WebSocketServer.sendInfo(WEB_STOCK_PROGRAM, type, data);
                break;

            case PUSH_RFID:
                businessHandle(sendEntity);
                break;
        }

    }

    @EventListener
    public void onCloseListener(WebSocketOnCloseEvent event) {
        WebSocketServer source = event.getSource();
        String sessionId = source.getSessionId();
        String userId = source.getUserId();

        JSONObject data = new JSONObject();
        data.put(FORWARD_USER_ID, userId);
        data.put(FORWARD_SESSION_ID, sessionId);
        data.put("type", 9);

        // 关闭rfid扫描
        WebSocketServer.sendInfo(WEB_STOCK_PROGRAM, 2, data);
        // 关闭定时任务
        CronUtil.remove(sessionId);
        // 删除数据
        map.remove(sessionId);
    }

    /**
     * 业务处理
     */
    private void businessHandle(MessageEntity sendEntity) {
        JSONObject data = sendEntity.getData();

        Integer businessType = data.getInteger("businessType");
        switch (businessType) {
            case 1:  // 入库
            case 2:  // 出库
                warehousingAndIssue(sendEntity, businessType);
                break;
            case 3:
                stockCheckrecordService.checkCallBack(data);
                break;
        }
    }

    // 入库锁
    private final Object warehousingLock = new Object();

    // 出库锁
    private final Object issueLock = new Object();

    /**
     * 入库和出库逻辑
     */
    private void warehousingAndIssue(MessageEntity sendEntity, Integer businessType) {
        JSONObject data = sendEntity.getData();
        String sessionId = sendEntity.getSessionId();
        String rfid = data.getString("rfid");

        MessageEntity messageEntity;
        synchronized (businessType == 1 ? warehousingLock : issueLock) {
            messageEntity = this.map.get(sessionId);

            if (messageEntity == null) {
                messageEntity = sendEntity;
                messageEntity.getData().remove("rfid");
                messageEntity.getData().put("rfidList", Collections.singletonList(rfid));
                this.map.put(sessionId, messageEntity);

                // 执行定时任务
                executeTask(sessionId, businessType);
            } else {
                List<String> rfidList = Convert.toList(String.class, messageEntity.getData().get("rfidList"));
                rfidList.add(rfid);
                messageEntity.getData().put("rfidList", rfidList);
            }
        }
    }

    /**
     * 执行定时任务
     */
    private void executeTask(String sessionId, Integer businessType) {
        CronUtil.schedule(sessionId, "0/3 * * * * *", () -> {
            MessageEntity entity = map.get(sessionId);
            JSONObject data = entity.getData();

            log.info("======================> executeTask:" + JSONObject.toJSONString(entity));

            List<String> rfidList = Convert.toList(String.class, data.get("rfidList"));
            List<String> lastRfidList = Convert.toList(String.class, data.get("lastRfidList"));
            if (rfidList.size() == lastRfidList.size()) return;

            data.put("lastRfidList", new ArrayList<>(rfidList));

            List<Map<String, Object>> rfidData;

            if (businessType.equals(1)) {
                rfidData = materialService.getInRfidData(rfidList);
            } else {
                rfidData = materialService.getOutRfidData(rfidList, data.getString("stockHouseId"));
            }

            data.put("rfidData", rfidData);

            log.info("======================> sendData：" + JSONObject.toJSONString(data));
            WebSocketServer.sendInfo(entity.getUserId(), entity.getSessionId(), entity.getType(), data);
        });
        CronUtil.setMatchSecond(true);
        CronUtil.start();
    }

}
