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

import com.tsi.app.common.utils.TimeUtils;
import com.tsi.comm.server.config.TraceConfig;
import com.tsi.comm.server.process.AbstractTsiCvimWorker;
import com.tsi.comm.server.protocol.AbstractTsiPacket;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

@Slf4j
@Getter
public class TsiCvimLoggingWorker extends AbstractTsiCvimWorker implements Runnable {

    private final LinkedBlockingQueue<AbstractTsiPacket> DATA_QUEUE;
//    private TsiNodeStatusRepository tsiNodeStatusRepository;
    private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public TsiCvimLoggingWorker(int idx, int qSize) {
        this.idx = idx;
        this.qSize = qSize;
        this.DATA_QUEUE = new LinkedBlockingQueue<>(qSize);
//        try {
//            this.tsiNodeStatusRepository = (TsiNodeStatusRepository) AppUtils.getBean(TsiNodeStatusRepository.class);
//        }
//        catch(Exception e) {
//            this.tsiNodeStatusRepository = null;
//            log.error("MongoDB: {}", e.getMessage());
//        }
        this.sdf.setTimeZone(java.util.TimeZone.getTimeZone("GMT+9"));
    }


    @Override
    public void run() {
        log.info("LoggingWorker({}): {} Start. QSIZE: {}", this.idx, Thread.currentThread().getName(), this.qSize);
        try {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    AbstractTsiPacket packet = this.DATA_QUEUE.poll(1, TimeUnit.SECONDS); // 인터럽트 감지 가능
                    if (packet != null) {
                        process(packet);
                    } else {
                        Thread.yield(); // 대기 중 CPU 양보
                    }
                } catch (InterruptedException ie) {
                    log.warn("LoggingWorker({}): {} Interrupted. Exiting...", this.idx, Thread.currentThread().getName());
                    Thread.currentThread().interrupt(); // 상태 복구
                    break;
                } catch (Exception e) {
                    log.error("LoggingWorker({}): {} Exception: {}", this.idx, Thread.currentThread().getName(), e.getMessage());
                }
            }
        } finally {
            log.info("LoggingWorker({}): {} Stopped.", this.idx, Thread.currentThread().getName());
        }
    }

//    @Override
//    public void run() {
//        log.info("{} Start. QSIZE: {}", Thread.currentThread().getName(), this.qSize);
//        while (true) {
//            try {
//                AbstractTsiPacket packet = this.DATA_QUEUE.take();
//                if (packet != null) {
//                    process(packet);
//                }
//            }
//            catch (Exception e) {
//                log.error("Exception(LoggingWorker while): {}", e.getMessage());
//            }
//        }
//    }

    /*
     *  작업큐에 데이터 추가
     */
    public boolean add(Object object) {
        boolean offer = false;
        AbstractTsiPacket packet = (AbstractTsiPacket)object;
        try {
            offer = this.DATA_QUEUE.offer(packet);  //offer => full -> return, add => full -> wait
            if (!offer) {
                MDC.put("id", Long.toString(packet.getNodeId()));
                log.error("LoggingWorker({}): Queue.offer(Full): {}, {}/{}/{}, {}, {}",
                        this.idx, packet.getNodeId(),
                        this.DATA_QUEUE.size(), this.qSize, this.DATA_QUEUE.remainingCapacity(),
                        TimeUtils.elapsedTime(packet.getRcv()), Thread.currentThread().getName());
                MDC.clear();
            }
        } catch (Exception e) {
            MDC.put("id", Long.toString(packet.getNodeId()));
            log.error("LoggingWorker({}): Queue.offer: Exception: {}, {}, {}", this.idx, packet.getNodeId(), Thread.currentThread().getName(), e.getMessage());
            MDC.clear();
        }
        return offer;
    }

    public void process(Object object) {
        AbstractTsiPacket packet = (AbstractTsiPacket)object;
//        long add = packet.getAdd() - packet.getRcv();
//        long pop = packet.getPop() - packet.getAdd();
//        long par = packet.getPar() - packet.getPop();
//        long kaf = packet.getEnd() - packet.getPar();
//        long mongo = 0, jobTotal = 0;
        long job = packet.getEnd() - packet.getRcv();

        Date date = new Date(packet.getTimespec().times() * 1000L);
        String collectTime = this.sdf.format(date);

//        if (this.tsiNodeStatusRepository != null && (packet instanceof TsiCpuPacket)) {
//            try {
//                long start = System.nanoTime();
//                TsiCpuPacket cpuPacket = (TsiCpuPacket) packet;
//                TcsNodeStatus mongoStatus = cpuPacket.getNodeStatus();
//                if (mongoStatus != null) {
//                    this.tsiNodeStatusRepository.save(mongoStatus);
//                    mongo = System.nanoTime() - start;
//                    packet.setEnd(System.nanoTime());
//                    jobTotal = packet.getEnd() - packet.getRcv();
//                }
//            }
//            catch (Exception e) {
//                log.error("Node: {}, MongoDB parsing error.", packet.getNodeId());
//            }
//        }

        MDC.put("id", Long.toString(packet.getNodeId()));
//        log.info("{} Node: {},      Q-Add: {} {}", collectTime, packet.getNodeId(), TimeUtils.elapsedTimeStr(add), Thread.currentThread().getName());
//        log.info("{} Node: {},      Q-Pop: {} {}", collectTime, packet.getNodeId(), TimeUtils.elapsedTimeStr(pop), Thread.currentThread().getName());
//        log.info("{} Node: {},    Parsing: {} {}", collectTime, packet.getNodeId(), TimeUtils.elapsedTimeStr(par), Thread.currentThread().getName());
//        log.info("{} Node: {},      kafka: {} {}", collectTime, packet.getNodeId(), TimeUtils.elapsedTimeStr(kaf), Thread.currentThread().getName());
        log.info("{} Node: {},        Job: {} {}, {} bytes, Average {}",
                collectTime,
                packet.getNodeId(),
                TimeUtils.elapsedTimeStr(job),
                Thread.currentThread().getName(),
                packet.getPacketLength(),
                TimeUtils.elapsedTimeStr(packet.getAvg()));
//        if (mongo > 0) {
//            log.info("{} Node: {},    MongoDb: {} {}", collectTime, packet.getNodeId(), TimeUtils.elapsedTimeStr(mongo), Thread.currentThread().getName());
//            log.info("{} Node: {},  Total Job: {} {}",
//                    collectTime,
//                    packet.getNodeId(),
//                    TimeUtils.elapsedTimeStr(jobTotal),
//                    Thread.currentThread().getName());
//        }

        //        log.info("{} Node: {}, ====", collectTime, packet.getNodeId());
        MDC.clear();
        calcProcessTime(packet.getRcv());
    }

    public void report() {
        log.info("LoggingWorker({}), Queue Total/Size/Remain: {}/{}/{}, Average: {}, {}",
                this.idx,
                this.qSize, this.DATA_QUEUE.size(), this.DATA_QUEUE.remainingCapacity(),
                TimeUtils.elapsedTimeStr(this.avgTime),
                Thread.currentThread().getName());
    }
}
