package com.kospo.talk.config.rabbit;

import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.kospo.talk.payload.TalkDto;
import lombok.RequiredArgsConstructor;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableRabbit
@RequiredArgsConstructor
public class RabbitConfig {

    @Value("${rabbitmq.connection.host}")
    private String host;
    @Value("${rabbitmq.connection.username}")
    private String username;
    @Value("${rabbitmq.connection.password}")
    private String password;
    @Value("${rabbitmq.talk.exchange}")
    private String talkExchange;
    @Value("${rabbitmq.talk.routing}")
    private String talkRouting;
    @Value("${rabbitmq.talk.queue}")
    private String talkQueue;
    @Value("${rabbitmq.user.exchange}")
    private String userExchange;
    @Value("${rabbitmq.user.routing}")
    private String userRouting;
    @Value("${rabbitmq.user.queue}")
    private String userQueue;
    @Value("${rabbitmq.file.exchange}")
    private String fileExchange;
    @Value("${rabbitmq.file.routing}")
    private String fileRouting;
    @Value("${rabbitmq.file.queue}")
    private String fileQueue;
    @Value("${rabbitmq.hub.exchange}")
    private String hubExchange;
    @Value("${rabbitmq.hub.routing}")
    private String hubRouting;
    @Value("${rabbitmq.hub.queue}")
    private String hubQueue;
    @Value("${rabbitmq.work.exchange}")
    private String workExchange;
    @Value("${rabbitmq.work.routing}")
    private String workRouting;
    @Value("${rabbitmq.work.queue}")
    private String workQueue;
    @Value("${rabbitmq.dead.exchange}")
    private String deadExchange;
    @Value("${rabbitmq.dead.queue}")
    private String deadQueue;
    private GenericObjectPool<Connection> connectionPool;

    @Bean
    public TopicExchange talkExchange() {
        return new TopicExchange(talkExchange);
    }

    @Bean
    public TopicExchange userExchange() {
        return new TopicExchange(userExchange);
    }

    @Bean
    public TopicExchange hubExchange() {
        return new TopicExchange(hubExchange);
    }

    @Bean
    public TopicExchange fileExchange() {
        return new TopicExchange(fileExchange);
    }

    @Bean
    public TopicExchange workExchange() {
        return new TopicExchange(workExchange);
    }

    @Bean
    public FanoutExchange deadExchange() {
        return new FanoutExchange(deadExchange);
    }

    private Map<String, Object> dlSetting() {
        Map<String, Object> setting = new HashMap();
        setting.put("x-dead-letter-exchange", deadExchange);
        setting.put("x-message-ttl", 30000);
        return setting;
    }

    @Bean
    public Queue deadQueue() {
        return new Queue(deadQueue, true, false, false);
    }

    @Bean
    public Queue talkQueue() {
        return new Queue(talkQueue, true, false, false, dlSetting());
    }

    @Bean
    public Queue userQueue() {
        return new Queue(userQueue, true, false, false, dlSetting());
    }

    @Bean
    public Queue fileQueue() {
        return new Queue(fileQueue, true, false, false, dlSetting());
    }

    @Bean
    public Queue hubQueue() {
        return new Queue(hubQueue, true, false, false, dlSetting());
    }

    @Bean
    public Queue workQueue() {
        return new Queue(workQueue, true, false, false, dlSetting());
    }


    @Bean
    public Binding talkBinding(TopicExchange talkExchange) {
        return BindingBuilder.bind(talkQueue()).to(talkExchange).with(talkRouting);
    }

    @Bean
    public Binding userBinding(TopicExchange userExchange) {
        return BindingBuilder.bind(userQueue()).to(userExchange).with(userRouting);
    }

    @Bean
    public Binding fileBinding(TopicExchange fileExchange) {
        return BindingBuilder.bind(fileQueue()).to(fileExchange).with(fileRouting);
    }

    @Bean
    public Binding hubBinding(TopicExchange hubExchange) {
        return BindingBuilder.bind(hubQueue()).to(hubExchange).with(hubRouting);
    }

    @Bean
    public Binding workBinding(TopicExchange workExchange) {
        return BindingBuilder.bind(workQueue()).to(workExchange).with(workRouting);
    }
    @Bean
    public Binding deadBinding(FanoutExchange deadExchange) {
        return BindingBuilder.bind(deadQueue()).to(deadExchange);
    }

    @Bean
    public RabbitTemplate talkTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory());
        rabbitTemplate.setMessageConverter(jsonMessageConverter());
        rabbitTemplate.setRoutingKey(talkRouting);
        return rabbitTemplate;
    }

    @Bean
    public RabbitTemplate userTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory());
        rabbitTemplate.setMessageConverter(jsonMessageConverter());
        rabbitTemplate.setRoutingKey(userRouting);
        return rabbitTemplate;
    }

    @Bean
    public RabbitTemplate fileTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory());
        rabbitTemplate.setMessageConverter(jsonMessageConverter());
        rabbitTemplate.setRoutingKey(fileRouting);
        return rabbitTemplate;
    }

    @Bean
    public RabbitTemplate hubTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory());
        rabbitTemplate.setMessageConverter(jsonMessageConverter());
        rabbitTemplate.setRoutingKey(hubRouting);
        return rabbitTemplate;
    }

    @Bean
    public RabbitTemplate workTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory());
        rabbitTemplate.setMessageConverter(jsonMessageConverter());
        rabbitTemplate.setRoutingKey(workRouting);
        return rabbitTemplate;
    }

    @Bean
    public CachingConnectionFactory cachingConnectionFactory() {
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setHost(host);
        factory.setPort(5672);
        factory.setUsername(username);
        factory.setPassword(password);
        factory.setConnectionTimeout(10000);
        factory.setChannelCacheSize(70);
        factory.setChannelCheckoutTimeout(15000);
        factory.setRequestedHeartBeat(15);
        factory.setCacheMode(CachingConnectionFactory.CacheMode.CHANNEL);
        factory.setCloseTimeout(15000);
//        factory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
        return factory;
    }

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(cachingConnectionFactory());
        factory.setRetryTemplate(retryTemplate());
        factory.setIdleEventInterval(60000L);
        factory.setConcurrentConsumers(10);  // 초기 소비자 수 설정
        factory.setMaxConcurrentConsumers(400);  // 최대 동시 소비자 수 설정
        factory.setBatchSize(20);
        factory.setPrefetchCount(100);
        return factory;
    }


    private RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();

        // 재시도 정책 설정
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3); // 최대 재시도 횟수
        retryTemplate.setRetryPolicy(retryPolicy);

        // 백오프 정책 설정 (지수 백오프)
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(1000); // 초기 간격 (밀리초)
        backOffPolicy.setMultiplier(2.0); // 간격 증가 배수
        backOffPolicy.setMaxInterval(10000); // 최대 간격 (밀리초)
        retryTemplate.setBackOffPolicy(backOffPolicy);


        return retryTemplate;
    }

//    메시지 리스너 대리자
    public static class Delegate {
        public void handleMessage(TalkDto talkDto) {
            if(talkDto != null) {
                System.out.println("delegate : " +talkDto.getMessage());
            }
        }
    }

    @Bean
    public Delegate delegate() {
        return new Delegate();
    }

    @Bean
    public MessageListener messageListener() {
        return  new MessageListenerAdapter(delegate(), jsonMessageConverter());
    }

//    @Bean
//    public SimpleMessageListenerContainer container() {
//        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
//        container.setConnectionFactory(connectionFactory());
//        container.setQueueNames(CHAT_QUEUE_NAME);
//        container.setMessageListener(new CustomMessageListener());
//        return container;
//    }




//    @Bean
//    public ConnectionFactory connectionFactory() {
//        CachingConnectionFactory factory = new CachingConnectionFactory();
//        factory.setHost("10.130.7.61");
//        factory.setPort(5672);
//        factory.setUsername(username);
//        factory.setPassword(password);
//        factory.setConnectionTimeout(2000);
//
//        return factory;
//    }

    @Bean
    public Jackson2JsonMessageConverter jsonMessageConverter(){
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
        objectMapper.registerModule(dateTimeModule());
        Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter(objectMapper);
        return converter;
    }

    @Bean
    public Module dateTimeModule() {
        return new JavaTimeModule();
    }

//    @Scheduled(fixedDelay = 30000L)
//    public void IdleThreadRemove() {
//        int count = 0;
//
//        Map<Thread, StackTraceElement[]> threads = Thread.getAllStackTraces();
//
//        for(Map.Entry<Thread, StackTraceElement[]> entry : threads.entrySet()) {
//            Thread thread = entry.getKey();
//            StackTraceElement[] stackTraceElements = entry.getValue();
//            if(thread.getName().contains("RabbitListenerEndpointContainer")) {
//                if(thread.getState() == Thread.State.WAITING || thread.getState() == Thread.State.TIMED_WAITING) {
//                    thread.interrupt();
//                    count++;
//                }
//
//            }
//        }
//        System.out.println("Interrupted " + count + " waiting RabbitMq Threads");
//    }
}
