package com.spring.infra.security.handler;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.ServletRequestPathUtils;

import lombok.RequiredArgsConstructor;

/**
 * HTTP 요청의 엔드포인트 존재 여부를 확인하는 클래스입니다.
 * 
 * <p>이 클래스는 주어진 HTTP 요청이 유효한 엔드포인트에 해당하는지 검사합니다.
 * Spring의 RequestMappingHandlerMapping을 사용하여 요청에 대한 핸들러를 조회합니다.</p>
 * 
 * <p>요청 경로가 파싱되지 않은 경우, 이를 파싱하고 캐시하여 이후 요청에서 재사용할 수 있도록 합니다.</p>
 * 
 * @author mindol
 * @version 1.0
 */
@Component
@RequiredArgsConstructor
public class HttpRequestEndpointChecker {

    /**
     * 요청 매핑 핸들러 매핑 객체입니다.
     * 
     * <p>Spring MVC의 요청 매핑을 처리하는 데 사용됩니다.</p>
     */
    @Qualifier("requestMappingHandlerMapping")
    private final RequestMappingHandlerMapping requestMapping;

    /**
     * 주어진 HTTP 요청이 유효한 엔드포인트인지 확인합니다.
     * 
     * <p>요청이 유효한 엔드포인트에 해당하면 true를 반환하고, 그렇지 않으면 false를 반환합니다.</p>
     * 
     * @param request 확인할 HTTP 요청 객체
     * @return 요청이 유효한 엔드포인트이면 true, 그렇지 않으면 false
     */
    public boolean isEndpointExist(HttpServletRequest request) {
        try {
            // 요청 경로가 파싱되지 않은 경우 파싱합니다.
            if (!ServletRequestPathUtils.hasParsedRequestPath(request)) {
                ServletRequestPathUtils.parseAndCache(request);
            }
            // 요청에 대한 핸들러를 조회합니다.
            HandlerExecutionChain handler = requestMapping.getHandler(request);
            if (handler != null) {
                HandlerMethod method = (HandlerMethod) handler.getHandler();
                // 핸들러가 존재하면 true를 반환합니다.
                if (method != null) return true;
            }
        } catch (Exception e) {
            // 예외 발생 시 false를 반환합니다.
            return false;
        }
        // 핸들러가 존재하지 않으면 false를 반환합니다.
        return false;
    }
}
