package com.spring.infra.quartz;

import java.util.Map;

import org.springframework.aop.support.AopUtils;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import com.spring.infra.batch.AbstractBatch;

import lombok.RequiredArgsConstructor;

/**
 * Quartz 작업 등록기 클래스입니다.
 * 
 * <p>주요 기능:</p>
 * <ul>
 *   <li>애플리케이션 컨텍스트 내의 모든 빈을 검사</li>
 *   <li>QuartzJob 어노테이션이 붙은 메소드 식별</li>
 *   <li>AbstractBatchJob을 상속받은 클래스 식별</li>
 *   <li>식별된 메소드를 Quartz 작업으로 등록</li>
 *   <li>각 작업에 대한 JobDetail 및 Trigger 생성</li>
 * </ul>
 * 
 * <p>이 클래스는 {@link ApplicationListener}를 구현하여
 * {@link ContextRefreshedEvent}가 발생할 때 자동으로 실행됩니다.</p>
 * 
 * @author mindol
 * @version 1.0
 */
@Component
@RequiredArgsConstructor
public class QuartzJobRegistrar implements ApplicationListener<ContextRefreshedEvent> {

    private final ApplicationContext applicationContext;
    private final QuartzScheduleService quartzScheduleService;

    /**
     * 애플리케이션 컨텍스트가 리프레시될 때 호출되는 메소드입니다.
     * QuartzJob 어노테이션이 붙은 메소드와 AbstractBatchJob을 상속받은 클래스를 등록합니다.
     *
     * @param event ContextRefreshedEvent 객체
     */
    @Override
    public void onApplicationEvent(@NonNull ContextRefreshedEvent event) {
        refreshJobs();
    }

    @EventListener
    public void onRefreshScopeRefreshed(@NonNull RefreshScopeRefreshedEvent event) {
        refreshJobs();
    }

    private void refreshJobs() {
        quartzScheduleService.clearJobs();
        registerBatchJobs();
    }

    /**
     * AbstractBatchJob을 상속받은 모든 클래스를 찾아 Quartz 스케줄러에 등록합니다.
     */
    private void registerBatchJobs() {
        Map<String, AbstractBatch> batchJobs = applicationContext.getBeansOfType(AbstractBatch.class);
        for (Map.Entry<String, AbstractBatch> entry : batchJobs.entrySet()) {
            AbstractBatch batch = entry.getValue();
            if (!AopUtils.isAopProxy(batch)) {
                quartzScheduleService.createJobTrigger(removeScopedTargetPrefix(entry.getKey()));
            }
        }
    }

    private String removeScopedTargetPrefix(String beanName) {
        return beanName.startsWith("scopedTarget.") ? beanName.substring("scopedTarget.".length()) : beanName;
    }

}
