package com.tsi.comm.server.config;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
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 ThreadPoolInitializer extends AsyncConfigurerSupport {

    private int poolCore = 0;

    @PostConstruct
    private void init() {
        int MAX_CORE = Math.max(8, Runtime.getRuntime().availableProcessors());
        if (this.poolCore <= 0) {
            this.poolCore = MAX_CORE;
        }
        if (this.poolCore < MAX_CORE) {
            this.poolCore = MAX_CORE;
        }

        log.info("[ThreadPoolInitializer]   availableProcessors: {}", MAX_CORE);
        log.info("[ThreadPoolInitializer]        threadPoolCore: {}", this.poolCore);
    }

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

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

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

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