package com.eactive.eai.rms.onl.common.service.ums;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import com.eactive.apim.portal.template.entity.MessageRequest;
import com.eactive.apim.portal.template.repository.MessageRequestRepository;
import com.eactive.eai.rms.common.datasource.DataSourceContextHolder;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.ext.djb.ums.service.UmsService;

import lombok.extern.slf4j.Slf4j;


@Slf4j
@Service
@Transactional(transactionManager = "transactionManagerForEMS")
public class UmsDispatchService {

    private final ThreadPoolExecutor executorService;
    private final TransactionTemplate transactionTemplate;

    private static final int CORE_POOL_SIZE = 5;
    private static final int MAX_POOL_SIZE = 20;
    private static final int QUEUE_CAPACITY = 1000;
    private static final long KEEP_ALIVE_TIME = 60L;

    @PersistenceContext(unitName = "entityManagerFactoryForEMS")
    private EntityManager entityManager;

    private final MessageRequestRepository messageRequestRepository;
    
    private final UmsService umsService;

    @Autowired
    public UmsDispatchService(
            @Qualifier("transactionManagerForEMS") PlatformTransactionManager transactionManager,
            MessageRequestRepository messageRequestRepository,
            UmsService umsService
    ) {
        this.messageRequestRepository = messageRequestRepository;
        this.umsService = umsService;

        this.executorService = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(QUEUE_CAPACITY),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        this.transactionTemplate = new TransactionTemplate(transactionManager);
        log.debug("UmsDispatchService initialized with thread pool - core: {}, max: {}, queue: {}",
                CORE_POOL_SIZE, MAX_POOL_SIZE, QUEUE_CAPACITY);
    }

    @PostConstruct
    public void init() {
        // KjbEmailSendModule에 repository 설정
//        KjbEmailSendModule.getInstance().setRepository(messageRequestRepository);
        log.info("UmsDispatchService initialized - KjbUmsService and KjbEmailSendModule ready via singleton");
    }
    

    @SuppressWarnings("unchecked")
	@Transactional(transactionManager = "transactionManagerForEMS")
    public void processMessages() {
        log.info("Starting to process pending messages");

        // 상태가 PENDING인 메시지들을 조회하고 바로 PROCESSING으로 변경
        List<MessageRequest> pendingMessages = entityManager.createQuery(
                        "SELECT m FROM MessageRequest m WHERE m.requestStatus = :status " +
                                "ORDER BY m.requestDate ASC")
                .setParameter("status", "PENDING")
                .setMaxResults(100)
                .getResultList();
        		
        if ( !pendingMessages.isEmpty()) {
            log.info("Found {} pending messages, updating status to PROCESSING", pendingMessages.size());

            // 조회된 메시지들의 상태를 일괄 PROCESSING으로 변경
            entityManager.createQuery(
                            "UPDATE MessageRequest m SET m.requestStatus = :newStatus " +
                                    "WHERE m IN :messages")
                    .setParameter("newStatus", "PROCESSING")
                    .setParameter("messages", pendingMessages)
                    .executeUpdate();

            // 영속성 컨텍스트 초기화 (벌크 연산 후)
            entityManager.flush();
            entityManager.clear();

            // 상태가 변경된 메시지들을 다시 조회
            List<MessageRequest> messagesToProcess = entityManager.createQuery(
                            "SELECT m FROM MessageRequest m WHERE m IN :messages")
                    .setParameter("messages", pendingMessages)
                    .getResultList();

            log.info("Starting to submit {} messages for processing", messagesToProcess.size());
            for (MessageRequest message : messagesToProcess) {
                submitMessageTask(message);
            }
        } else {
            log.info("No pending messages found");

            // 발송 메세지가 없을 때 파일 삭제 처리 진행

        }
    }


    private void submitMessageTask(MessageRequest message) {
        int queueSize = executorService.getQueue().size();
        log.debug("Current queue size before submission: {}", queueSize);

        executorService.submit(() -> {
            try {
                transactionTemplate.execute(status -> {
                    try {
                    	umsService.send(message);
                        updateMessageStatus(message, "SENT");
                        return null;
                    } catch (Exception e) {
                        // checked exception을 RuntimeException으로 감싸 전파 → TransactionTemplate이 rollback 후 re-throw
                        throw new RuntimeException(e);
                    }
                });
            } catch (Exception e) {
                log.error("Failed to process message: {} - Error: {}", message.getId(), e.getMessage(), e);
                try {
                    transactionTemplate.execute(s -> {
                        updateMessageStatus(message, "FAILED");
                        return null;
                    });
                } catch (Exception ex) {
                    log.error("Failed to update FAIL status for message: {}", message.getId(), ex);
                }
            }
        });
    }

    

    public void updateMessageStatus(MessageRequest message, String status) {
        message.setRequestStatus(status);
        message.setSentDate(LocalDateTime.now());
        entityManager.merge(message);
        entityManager.flush();
    }
}