package org.wcs.quartzJob;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.wcs.annotation.ScanMethodTag;
import org.wcs.business.stock.intf.IScanMethod;
import org.wcs.constant.enums.common.TrueOrFalseEnum;
import org.wcs.constant.enums.database.StockScanTypeEnum;
import org.wcs.mapper.intf.AppStockScanService;
import org.wcs.model.bo.tuple.Tuple2;
import org.wcs.model.po.app.AppStockScan;
import org.wcs.plugin.plc.PlcCommunicationFactory;
import org.wcs.plugin.plc.model.ScanInfo;
import org.wcs.utils.AppStringUtils;
import org.wcs.utils.AppThreadUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 扫码器定时任务类
 */
@Slf4j
@DisallowConcurrentExecution
@RequiredArgsConstructor
public class ScannerJob  implements Job {

    private final AppStockScanService stockScanService;
    private final StringRedisTemplate stringRedisTemplate;
    private final IScanMethod scanMethod;

    public static final String SCANNER_DATA_REDIS_KEY = "WCS:CACHE:SCANNER_JOB_KEY";
    private static Map<String, Method> scanMethodMap = new ConcurrentHashMap<>();

    @Override
    public void execute(JobExecutionContext jobExecutionContext) {
        // 先获取扫码方法，后续还会有获取
        if(scanMethodMap.isEmpty()) {
            getScanMethod();
        }
        // 获取扫码数据
        List<AppStockScan> stockScans; // 扫码数据
        String scanDataCache = stringRedisTemplate.opsForValue().get(SCANNER_DATA_REDIS_KEY);
        if(AppStringUtils.isEmpty(scanDataCache)) {
            AppStockScan queryParam = new AppStockScan();
            queryParam.setScanStatus(TrueOrFalseEnum.TRUE.getCode());
            queryParam.setScanType(StockScanTypeEnum.PLC_SCAN.getCode());
            stockScans = stockScanService.query(queryParam);
            stringRedisTemplate.opsForValue().set(SCANNER_DATA_REDIS_KEY, AppStringUtils.objectToString(stockScans), 10, TimeUnit.SECONDS);
        } else {
            stockScans = JSONObject.parseObject(scanDataCache, new TypeReference<>(){});
        }
        if(stockScans == null || stockScans.isEmpty()) {
            return;
        }
        List<Runnable> runnableList = new ArrayList<>();
        for (AppStockScan stockScan : stockScans) {
            runnableList.add(() -> {
                if(AppStringUtils.isEmpty(stockScan.getScanId()) || AppStringUtils.isEmpty(stockScan.getScanMethod())) {
                    return;
                }
                Tuple2<String, ScanInfo> stringScanInfoTuple2 = PlcCommunicationFactory.getPlcCommunicationByPlcId(stockScan.getPlcId()).readScannerInfo(stockScan.getScanId());
                if(stringScanInfoTuple2.item1 != null || stringScanInfoTuple2.item2 == null) {
                    log.error("扫码器[{}]信息读取失败: {}", stockScan.getScanId(), stringScanInfoTuple2.item1);
                    return;
                }
                ScanInfo scanInfo = stringScanInfoTuple2.item2;
                if(scanMethodMap.isEmpty()) {
                    getScanMethod();
                }
                if(scanMethodMap.isEmpty()) {
                    log.error("扫码器[{}]信息处理失败: {}", stockScan.getScanId(), "未找到扫码方法");
                    return;
                }
                Method method = scanMethodMap.get(stockScan.getScanMethod());
                if(method == null) {
                    log.error("扫码器[{}]信息处理失败: {}，扫码方法不存在", stockScan.getScanId(), stockScan.getScanMethod());
                    return;
                }
                try {
                    method.invoke(scanMethod, stockScan, scanInfo);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    log.error("扫码器[{}]信息处理失败: {}，扫码方法调用失败", stockScan.getScanId(), stockScan.getScanMethod());
                }
            });
        }
        AppThreadUtils.waitAllTaskComplete(runnableList);
    }

    /**
     * 获取扫码方法
     */
    private synchronized void getScanMethod() {
        if(scanMethod == null) {
            return;
        }
        scanMethodMap = new ConcurrentHashMap<>();
        Class<? extends IScanMethod> scanMethodClass = scanMethod.getClass();
        Method[] methods = scanMethodClass.getDeclaredMethods();
        for (Method method : methods) {
            ScanMethodTag scanMethodTag = method.getAnnotation(ScanMethodTag.class);
            if(scanMethodTag != null) {
                String value = scanMethodTag.value();
                scanMethodMap.put(value, method);
            }
        }
    }
}
