package incheon.com.cmm.interceptor;

import incheon.com.cmm.context.RequestContext;
import incheon.com.menu.annotation.MenuCode;
import incheon.com.menu.mapper.CommonMenuMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * RequestContext 보강 인터셉터
 *
 * RequestContextFilter 이후 추가 컨텍스트 정보를 보강:
 * - 메뉴 정보 (메뉴 코드, 메뉴명)
 * - 시스템 정보 (시스템명)
 * - 향후 권한 정보 추가 예정
 *
 * 메뉴 코드 결정 방식 (2단계 fallback):
 * 1순위: @MenuCode 어노테이션 (Controller/Method 레벨)
 * 2순위: DB 기반 URL 패턴 매칭
 *
 * 실행 순서:
 * RequestContextFilter → RequestContextInterceptor → Controller
 *
 * @author Incheon Geo Platform
 * @since 2025-10-29
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RequestContextInterceptor implements HandlerInterceptor {

    private final CommonMenuMapper menuMapper;

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {
        String currentMenuCd = null;

        try {
            // 0순위: HTTP 헤더에서 메뉴 코드 확인 (API 요청용 - ICAPP.fetch에서 전달)
            String headerMenuCd = request.getHeader("X-Menu-Code");
            if (headerMenuCd != null && !headerMenuCd.trim().isEmpty()) {
                currentMenuCd = headerMenuCd.trim();
                log.debug("HTTP 헤더(X-Menu-Code)에서 메뉴 코드 추출: {}", currentMenuCd);
                RequestContext.setMenuInfo(currentMenuCd, null, null);
                return true; // 조기 반환 (DB 조회 불필요)
            }

            // 1순위: @MenuCode 어노테이션 체크
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;

                // 메서드 레벨 어노테이션 우선
                MenuCode menuCode = handlerMethod.getMethodAnnotation(MenuCode.class);

                // 클래스 레벨 어노테이션 fallback
                if (menuCode == null) {
                    menuCode = handlerMethod.getBeanType().getAnnotation(MenuCode.class);
                }

                if (menuCode != null) {
                    currentMenuCd = menuCode.value();
                    log.debug("@MenuCode 어노테이션에서 메뉴 코드 추출: {}", currentMenuCd);
                }
            }

            // 2순위: DB 조회 - URL 패턴 매칭
            if (currentMenuCd == null) {
                String uri = request.getRequestURI();

                // RequestContext에서 시스템 코드 조회 (중복 제거)
                String sysCd = RequestContext.getCurrentSysCd();

                log.info("=== 메뉴 코드 조회: URI={}, 시스템코드={}", uri, sysCd);

                if (sysCd != null) {
                    Map<String, Object> params = new HashMap<>();
                    params.put("sysCd", sysCd);
                    params.put("requestUri", uri);

                    try {
                        // 메뉴 정보 조회 (메뉴코드, 메뉴명, 시스템명)
                        Map<String, Object> menuInfo = menuMapper.selectMenuInfoByUrlPattern(params);
                        if (menuInfo != null) {
                            currentMenuCd = (String) menuInfo.get("menu_cd");
                            String menuNm = (String) menuInfo.get("menu_nm");
                            String sysNm = (String) menuInfo.get("sys_nm");

                            // RequestContext에 메뉴 정보 일괄 설정
                            RequestContext.setMenuInfo(currentMenuCd, menuNm, sysNm);

                            log.info("=== URL 패턴 매칭 성공: {} (sysCd={}) -> 메뉴코드={}, 메뉴명={}, 시스템명={}",
                                    uri, sysCd, currentMenuCd, menuNm, sysNm);
                        } else {
                            log.warn("=== URL 패턴 매칭 실패: {} (sysCd={}) -> 메뉴를 찾지 못함", uri, sysCd);
                        }
                    } catch (Exception e) {
                        log.error("=== URL 패턴 기반 메뉴 조회 중 오류 발생: {} (sysCd={})", uri, sysCd, e);
                    }
                } else {
                    log.warn("=== 시스템 코드가 설정되지 않음: URI={}", uri);
                }
            } else {
                // @MenuCode 어노테이션으로 메뉴 코드만 설정된 경우
                RequestContext.setMenuInfo(currentMenuCd, null, null);
            }

        } catch (Exception e) {
            log.error("RequestContext 인터셉터 처리 중 오류 발생", e);
        }

        return true;
    }
}
