package com.tsi.comm.server.process.dbms;

import com.tsi.comm.server.config.TsiCvimServerConfig;
import com.tsi.comm.server.process.AbstractTsiCvimProcess;
import com.tsi.comm.server.process.AbstractTsiCvimWorker;
import com.tsi.comm.server.process.packet.TsiCvimPacketWorker;
import com.tsi.comm.server.repository.TsiNodeManager;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

@Slf4j
@Getter
@RequiredArgsConstructor
@Service
public class TsiCvimDbmsProcess extends AbstractTsiCvimProcess {

    private final TsiCvimServerConfig config;
    private final TsiNodeManager nodeManager;
    private ExecutorService executor = null;
    private int qSize;

    public void start() {
        final ThreadGroup workerGroup = new ThreadGroup("dbmsProcess");
        this.workers = this.config.getDbmsWorkers();
        this.qSize = Math.max(10, this.nodeManager.size());
        if (this.workers == 1) {
            this.qSize *= 3;
        }

        this.executor = Executors.newFixedThreadPool(this.workers, new ThreadFactory() {
            private int count = 0;
            public Thread newThread(Runnable r) {
                Thread t = new Thread(workerGroup, r);
                t.setName(String.format("dbmsWorker-%02d.%02d", workers, ++count));
                t.setDaemon(true);
                return t;
            }
        });

        for (int ii = 0; ii < this.workers; ii++) {
            TsiCvimDbmsWorker dbmsWorker = new TsiCvimDbmsWorker(ii, this.qSize);
            this.workerList.add(dbmsWorker);
            this.executor.submit(dbmsWorker); // 여기서 실행됨!
        }
    }

    public boolean add(Object object, int idx) {
        boolean offer = false;
        try {
            if (idx >= 0 && idx < this.workers) {
                offer = this.workerList.get(idx).add(object);
            }
            else {
                log.error("DbmsProcess: Invalid packet queue index: {}", idx);
            }
        }
        catch (Exception e) {
            log.error("DbmsProcess: QUEUE_DATA.add: Exception: {}, {}", object.toString(), e.getMessage());
        }
        return offer;
    }

    public void stop() {
        log.info("DbmsProcess Stopping...");

        for (AbstractTsiCvimWorker worker : this.workerList) {
            try {
                ((TsiCvimDbmsWorker) worker).getDATA_QUEUE().put(TsiCvimDbmsWorker.SHUTDOWN_PACKET);
            } catch (InterruptedException e) {
                log.warn("DbmsProcess: Interrupted while sending shutdown signal to worker {}.", worker.getIdx());
                Thread.currentThread().interrupt();
            }
        }

        this.executor.shutdown();

        try {
            if (!this.executor.awaitTermination(3, TimeUnit.SECONDS)) {
                this.executor.shutdownNow(); // 실행 중인 작업도 강제 중단 시도

                // 큐에 남아있는 데이터 로깅
                int remainingTasks = 0;
                for (AbstractTsiCvimWorker worker : this.workerList) {
                    remainingTasks += ((TsiCvimPacketWorker) worker).getDATA_QUEUE().size();
                }
                if (remainingTasks > 0) {
                    log.warn("DbmsProcess: queue remain {}, forced shutdown.", remainingTasks);
                }

                // 강제 종료 후에도 종료될 때까지 조금 더 기다려줌
                if (!this.executor.awaitTermination(1, TimeUnit.SECONDS)) {
                    log.error("DbmsProcess: Executor did not terminate even after forced shutdown.");
                }
            }
        } catch (InterruptedException e) {
            this.executor.shutdownNow();
            Thread.currentThread().interrupt(); // 현재 스레드도 중단
        }

        log.info("DbmsProcess Stopped...");
    }

    public void report() {
        for (AbstractTsiCvimWorker worker : this.workerList) {
            worker.report();
        }
    }
}
