package com.eactive.ext.kjb.common;

import lombok.extern.slf4j.Slf4j;

import java.util.function.Function;

/**
 * KjbProperty 싱글톤 홀더.
 *
 * <p>모든 곳에서 {@code KjbPropertyHolder.get()}으로 현재 KjbProperty에 접근합니다.
 * eapim-admin에서 초기화 시 {@code initialize()}를 호출하고,
 * MonitoringContext.refresh() 시 {@code reload()}를 호출하여 값을 갱신합니다.</p>
 *
 * <p>사용 예시:</p>
 * <pre>
 * // 값 접근
 * String url = KjbPropertyHolder.get().getUmsHostUrl();
 *
 * // 초기화 (eapim-admin에서)
 * KjbPropertyInjector injector = new KjbPropertyInjector(service, "Monitoring");
 * KjbPropertyHolder.initialize(injector::inject);
 *
 * // reload (MonitoringContext.refresh() 시)
 * KjbPropertyHolder.reload();
 *
 * // 테스트용
 * KjbPropertyHolder.setForTest(mockProperty);
 * </pre>
 */
@Slf4j
public class KjbPropertyHolder {

    private static volatile KjbProperty instance = new KjbProperty();
    private static volatile Function<KjbProperty, KjbProperty> injector;

    private KjbPropertyHolder() {
        // 유틸리티 클래스
    }

    /**
     * 현재 KjbProperty 인스턴스를 반환합니다.
     */
    public static KjbProperty get() {
        return instance;
    }

    /**
     * 초기화 함수를 등록하고 최초 로딩을 수행합니다.
     * eapim-admin 부팅 시 MonitoringContextImpl.init()에서 호출됩니다.
     *
     * @param injectorFn KjbProperty를 DB에서 로딩하는 함수 (KjbPropertyInjector::inject)
     */
    public static void initialize(Function<KjbProperty, KjbProperty> injectorFn) {
        injector = injectorFn;
        reload();
        log.info("KjbPropertyHolder 초기화 완료");
    }

    /**
     * DB에서 프로퍼티를 다시 로딩합니다.
     * MonitoringContext.refresh() 호출 시 함께 호출됩니다.
     */
    public static void reload() {
        if (injector != null) {
            try {
                instance = injector.apply(new KjbProperty());
                log.info("KjbProperty reload 완료");
            } catch (Exception e) {
                log.error("KjbProperty reload 실패: {}", e.getMessage(), e);
            }
        } else {
            log.debug("KjbPropertyHolder: injector가 설정되지 않음 (테스트 환경일 수 있음)");
        }
    }

    /**
     * 테스트용 - 직접 인스턴스를 설정합니다.
     */
    public static void setForTest(KjbProperty testProperty) {
        instance = testProperty;
        log.debug("KjbProperty 테스트 인스턴스 설정");
    }

    /**
     * 테스트용 - 기본값으로 초기화합니다.
     */
    public static void resetForTest() {
        instance = new KjbProperty();
        injector = null;
        log.debug("KjbPropertyHolder 테스트 초기화");
    }
}
