package com.tsi.comm.server.config;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.annotation.PostConstruct;
import java.util.concurrent.Executor;

@EqualsAndHashCode(callSuper = true)
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "application.thread-pool")
public class TsiThreadPoolConfig extends AsyncConfigurerSupport {

    private int poolCore = 0;
    private int MAX_CORE = Runtime.getRuntime().availableProcessors();

    @PostConstruct
    private void init() {
        if (this.MAX_CORE < 8) {
            this.MAX_CORE = 16;
        }

        if (this.poolCore < this.MAX_CORE) {
            this.poolCore = this.MAX_CORE;
        }
        log.info("[{}]   availableProcessors: {}", this.getClass().getSimpleName(), this.MAX_CORE);
        log.info("[{}]              MAX_CORE: {}", this.getClass().getSimpleName(), this.MAX_CORE);
        log.info("[{}]        threadPoolCore: {}", this.getClass().getSimpleName(), this.poolCore);
        log.info("[{}]          CorePoolSize: {} EA.", this.getClass().getSimpleName(), this.poolCore);
        log.info("[{}]           MaxPoolSize: {} EA.", this.getClass().getSimpleName(), getMaxPoolSize());
        log.info("[{}]         QueueCapacity: {} EA.", this.getClass().getSimpleName(), getQueueCapacity());
    }
    public int getMaxPoolSize() {
        return this.poolCore * 2;
    }

    public int getQueueCapacity() {
        return this.poolCore * 4;
    }

    public ThreadPoolTaskExecutor getDefaultExecutor(int poolSize) {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(poolSize);               // 인스턴스 되면서 기본적으로 띄울 스레드 개수.
                                                                        // 아무작업이 없어도 corePoolSize 만큼 스레드가 생성
        threadPoolTaskExecutor.setMaxPoolSize(getMaxPoolSize());        // 풀 최대개수, Queue Capacity 까지 꽉차는 경우 maxPoolSize 만큼 넓혀감
        threadPoolTaskExecutor.setQueueCapacity(getQueueCapacity());    // 스레드 대기큐, Queue Capacity 가 꽉차면 스레드가 추가로 생성됨. Async 처리시 Queue Size
                                                                        // (설정하지 않으면 Integer.MAX 이기 때문에 성능에 문제가 발생함)
        return threadPoolTaskExecutor;
    }

    @Bean(name="queueJobExecutor")
    public Executor getQueueJobExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = getDefaultExecutor(this.poolCore);
        threadPoolTaskExecutor.setThreadNamePrefix("queue-pool-");
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

    @Bean(name="dbmsJobExecutor")
    public Executor getDbmsJobExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = getDefaultExecutor(this.poolCore);
        threadPoolTaskExecutor.setThreadNamePrefix("dbms-pool-");
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = getDefaultExecutor(this.poolCore*2);
        threadPoolTaskExecutor.setThreadNamePrefix("tsi-pool-");
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}
