package com.spring.common.util;

import java.util.Optional;
import java.util.function.Function;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * 서블릿 관련 유틸리티 클래스입니다.
 * 
 * <p>현재 요청의 HttpServletRequest, HttpServletResponse, HttpSession 등에 접근하는 메서드를 제공합니다.</p>
 * 
 * @author mindol
 * @version 1.0
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ServletUtils {

    /**
     * 현재 요청의 ServletRequestAttributes를 Optional로 반환합니다.
     *
     * @return ServletRequestAttributes를 포함한 Optional 객체
     */
    private static Optional<ServletRequestAttributes> getAttributes() {
        return Optional.ofNullable(RequestContextHolder.getRequestAttributes())
            .filter(ServletRequestAttributes.class::isInstance)
            .map(ServletRequestAttributes.class::cast);
    }

    /**
     * ServletRequestAttributes에서 특정 속성을 추출합니다.
     *
     * @param getter ServletRequestAttributes에서 원하는 속성을 추출하는 함수
     * @param <T> 반환될 속성의 타입
     * @return 추출된 속성 또는 null
     */
    private static <T> T getAttribute(Function<ServletRequestAttributes, T> getter) {
        return getAttributes().map(getter).orElse(null);
    }

    /**
     * 현재 요청의 HttpServletRequest를 반환합니다.
     *
     * @return 현재 HttpServletRequest 또는 null
     */
    public static HttpServletRequest getRequest() {
        return getAttribute(ServletRequestAttributes::getRequest);
    }

    /**
     * 현재 요청의 속성을 설정합니다.
     *
     * @param key 속성의 키
     * @param obj 설정할 속성 값
     */
    public static void setRequestAttribute(String key, Object obj) {
        getAttributes().ifPresent(attr -> attr.setAttribute(key, obj, RequestAttributes.SCOPE_REQUEST));
    }

    /**
     * 현재 요청의 속성을 반환합니다.
     *
     * @param key 속성의 키
     * @return 요청 속성 값 또는 null
     */
    public static Object getRequestAttribute(String key) {
        return getAttribute(attr -> attr.getAttribute(key, RequestAttributes.SCOPE_REQUEST));
    }

    /**
     * 현재 요청의 HttpSession을 반환합니다.
     *
     * @return 현재 HttpSession 또는 null
     */
    public static HttpSession getSession() {
        return Optional.ofNullable(getRequest()).map(HttpServletRequest::getSession).orElse(null);
    }

    /**
     * 현재 세션의 속성을 설정합니다.
     *
     * @param key 속성의 키
     * @param obj 설정할 속성 값
     */
    public static void setSessionAttribute(String key, Object obj) {
        getAttributes().ifPresent(attr -> attr.setAttribute(key, obj, RequestAttributes.SCOPE_SESSION));
    }

    /**
     * 현재 세션의 속성을 반환합니다.
     *
     * @param key 속성의 키
     * @return 세션 속성 값 또는 null
     */
    public static Object getSessionAttribute(String key) {
        return getAttribute(attr -> attr.getAttribute(key, RequestAttributes.SCOPE_SESSION));
    }

    /**
     * 현재 요청의 HttpServletResponse를 반환합니다.
     *
     * @return 현재 HttpServletResponse 또는 null
     */
    public static HttpServletResponse getResponse() {
        return getAttribute(ServletRequestAttributes::getResponse);
    }
    
}
