package com.aip.gateway.api.process;

import com.aip.gateway.api.app.AppUtils;
import com.aip.gateway.api.config.ThreadPoolInitializer;
import com.aip.gateway.api.dao.mapper.BaseDbMapper;
import com.aip.gateway.api.entity.TbAipCallLog;
import com.aip.gateway.api.entity.TbAipFileJobLog;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@RequiredArgsConstructor
@Service
public class DbmsDataProcess {

    private final BaseDbMapper mapper;
    private final LinkedBlockingQueue<DbmsData> dbmsDataBlockingQueue = new LinkedBlockingQueue<>(1000);
    private final ThreadPoolExecutor taskExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);

    private final DbmsDataAsyncTask asyncTask;

    private int maxCore = Runtime.getRuntime().availableProcessors();

    public void run() {
        log.info("DbmsDataProcess.run: Start.");
        if (this.maxCore < 8) {
            this.maxCore = 8;
        }
        ThreadPoolInitializer poolInitializer = (ThreadPoolInitializer) AppUtils.getBean(ThreadPoolInitializer.class);
        int executePool = Math.max(this.maxCore, poolInitializer.getWork());
        for (int ii = 0; ii < executePool; ii++) {
            log.info("DbmsDataProcess.Task: {}", ii);
            this.taskExecutor.execute(() -> {
                boolean isRunning = true;
                while (isRunning) {
                    try {
                        DbmsData data = dbmsDataBlockingQueue.take();
                        asyncTask.run(this, data);
                    }
                    catch (Exception e) {
                        log.error("DbmsDataProcess.Task: Exception: {}", e.getMessage(), e);
                        Thread.currentThread().interrupt();
                        isRunning = false;
                    }
                }
            });
        }

        log.info("DbmsDataProcess.run: ..End.");
    }

    /**
     * 비동기 타스크에서 실행되는 함수.
     * 클래스가 달라야 비동기 타스크로 실행된다.
     * @param data
     */
    public void runJob(DbmsData data) {
        //if (data.getType() == DbmsDataType.DBMS_DATA_UNIT_SYST_STTS) {
        //}
        //else {
            process(data);
        //}
    }

    public void process(DbmsData data) {
        int cnt = 0;
        try {
            DbmsDataType type = data.getType();
            switch(type) {
                case DBMS_DATA_API_CALL_LOG:
                    TbAipCallLog callLog = (TbAipCallLog)data.getData();
                    cnt = this.mapper.insertAipCallLog(callLog);
                    log.info("DBMS_DATA_API_CALL_LOG: {} EA.", cnt);
                    break;
                case DBMS_DATA_AIP_FILE_JOB_LOG:
                    TbAipFileJobLog fileJobLog = (TbAipFileJobLog)data.getData();
                    cnt = this.mapper.insertAipFileJobLog(fileJobLog);
                    log.info("DBMS_DATA_AIP_FILE_JOB_LOG: {} EA.", cnt);
                    break;
                default:
                    log.error("DbmsJobProcess.process: Unknown Request {}.", type);
                    break;
            }
        } catch (Exception e) {
            log.error("DbmsJobProcess.process: Exception: {}", e.toString());
        }
    }

    /*
     *  작업큐에 데이터 추가
     */
    public boolean add(DbmsData data) {
        boolean offer = false;
        try {
            //offer => full -> return
            //add   => full -> wait
            //큐가 차더라도 바로 리턴함.
            offer = dbmsDataBlockingQueue.offer(data);
            if (!offer) {
                log.error("DbmsDataProcess.add: Queue Full Error, Size: {} EA", dbmsDataBlockingQueue.size());
            }
        } catch (Exception e) {
            log.error("DbmsDataProcess.add: Exception: {}", e.getMessage(), e);
        }
        return offer;
    }

}
