package com.spring.infra.db.orm.jpa;

import javax.persistence.Entity;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * JPA 엔티티 클래스를 스캔하는 유틸리티 클래스입니다.
 * 
 * <p>이 클래스는 지정된 패키지 내의 엔티티 클래스를 스캔하고,
 * 선택적으로 특정 데이터베이스에 해당하는 엔티티만 필터링할 수 있습니다.</p>
 * 
 * @author mindol
 * @version 1.0
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class EntityScanner {

    /**
     * 지정된 기본 패키지 내의 모든 엔티티를 스캔합니다.
     * 
     * @param basePackage 스캔할 기본 패키지
     * @return 스캔된 엔티티 클래스의 패키지 이름 배열
     */
    public static String[] scanEntities(String basePackage) {
        return scanEntities(basePackage, null);
    }
    
    /**
     * 지정된 기본 패키지 내의 엔티티를 스캔하고, 선택적으로 특정 데이터베이스에 해당하는 엔티티만 필터링합니다.
     * 
     * @param basePackage 스캔할 기본 패키지
     * @param dbName 필터링할 데이터베이스 이름 (null이면 모든 엔티티 반환)
     * @return 스캔된 엔티티 클래스의 패키지 이름 배열
     */
    public static String[] scanEntities(String basePackage, String dbName) {
        var scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter((MetadataReader reader, MetadataReaderFactory factory) -> {
            if (!reader.getAnnotationMetadata().hasAnnotation(Entity.class.getName())) {
                return false;
            }
            if (dbName != null) {
                var attributes = reader.getAnnotationMetadata().getAnnotationAttributes(DatabaseSelector.class.getName());
                return attributes != null && dbName.equals(attributes.get("value"));
            }
            return !reader.getAnnotationMetadata().hasAnnotation(DatabaseSelector.class.getName());
        });
        
        return scanner.findCandidateComponents(basePackage).stream()
                .map(BeanDefinition::getBeanClassName)
                .map(className -> className.substring(0, className.lastIndexOf('.')))
                .distinct()
                .toArray(String[]::new);
    }

}
