package com.spring.infra.security.handler;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;

import com.spring.infra.security.error.SecurityAuthException;
import com.spring.infra.security.error.SecurityExceptionRule;

import lombok.RequiredArgsConstructor;

/**
 * 접근 거부 상황을 처리하는 핸들러 클래스입니다.
 * 
 * <p>이 클래스는 사용자가 인증되었지만 특정 리소스에 대한 접근 권한이 없는 경우를 처리합니다.
 * 이 경우 SC_FORBIDDEN (403) 응답을 반환합니다.</p>
 * 
 * <p>API 요청과 일반 요청을 구분하여 각각에 대해 적절한 응답을 생성합니다.</p>
 * 
 * @author mindol
 * @version 1.0
 */
@Component
@RequiredArgsConstructor
public class SecurityAccessDeniedHandler implements AccessDeniedHandler {

    /**
     * 애플리케이션 컨텍스트 객체입니다.
     * 
     * <p>Spring의 ApplicationContext를 사용하여 필요한 빈을 가져옵니다.</p>
     */
    private final ApplicationContext applicationContext;

    /**
     * 접근 거부 상황을 처리합니다.
     * 
     * <p>HTTP 요청을 검사하여 API 요청인 경우 HandlerExceptionResolver를 사용하여
     * 예외를 처리하고, 일반 요청인 경우 403 Forbidden 응답을 반환합니다.</p>
     * 
     * @param request HTTP 요청 객체
     * @param response HTTP 응답 객체
     * @param accessDeniedException 접근 거부 예외
     * @throws IOException 입출력 예외
     * @throws ServletException 서블릿 예외
     */
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException accessDeniedException) throws IOException, ServletException {
        if (isApiRequest(request)) {
            // API 요청인 경우 예외를 처리합니다.
            HandlerExceptionResolver resolver = applicationContext.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
            resolver.resolveException(request, response, null, new SecurityAuthException(SecurityExceptionRule.USER_FORBIDDEN));
        } else {
            // 일반 요청인 경우 403 Forbidden 응답을 설정합니다.
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpServletResponse.SC_FORBIDDEN);
            request.setAttribute(RequestDispatcher.ERROR_MESSAGE, SecurityExceptionRule.USER_FORBIDDEN.getMessage());
            RequestDispatcher dispatcher = request.getRequestDispatcher("/error");
            dispatcher.forward(request, response);
        }
    }

    /**
     * 요청이 API 요청인지 확인합니다.
     * 
     * <p>HTTP 요청의 Accept 헤더를 검사하여 JSON 형식의 응답을 요구하는지 확인합니다.</p>
     * 
     * @param request HTTP 요청 객체
     * @return API 요청이면 true, 그렇지 않으면 false
     */
    private boolean isApiRequest(HttpServletRequest request) {
        String accept = request.getHeader(HttpHeaders.ACCEPT);
        return accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE);
    }
    
}
