package incheon.com.cmm.web;

import incheon.ags.ias.comCd.service.ComCdService;
import incheon.ags.ias.comCd.vo.ComCdVO;
import incheon.com.cmm.context.RequestContext;
import incheon.com.menu.service.CommonMenuService;
import incheon.com.menu.vo.HeaderMenuVO;
import incheon.com.security.vo.LoginVO;
import incheon.com.security.vo.UserRoleVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 메뉴 컨트롤러 어드바이스
 *
 * 모든 @Controller 요청에 자동으로 메뉴 데이터 및 현재 메뉴 코드 바인딩
 */
@Slf4j
@ControllerAdvice(annotations = Controller.class)
@RequiredArgsConstructor
public class MenuControllerAdvice {

    private final CommonMenuService menuService;
    private final ComCdService comCdService;

    /**
     * 유효한 시스템 코드 목록 (애플리케이션 시작 시 DB에서 로드)
     */
    private Set<String> validSysCodes = new HashSet<>();

    /**
     * 애플리케이션 시작 시 시스템 코드 목록을 DB에서 로드
     */
    @PostConstruct
    public void init() {
        try {
            refreshSystemCodes();
        } catch (Exception e) {
            throw new IllegalStateException("시스템 코드 초기화 실패. DB 연결을 확인하세요.", e);
        }
    }

    /**
     * DB에서 활성화된 시스템 코드 목록을 조회하여 캐싱
     */
    private void refreshSystemCodes() throws Exception {
        List<ComCdVO> systemCodes = comCdService.getSystemCodeList();
        validSysCodes = systemCodes.stream()
                .map(ComCdVO::getCd)
                .collect(Collectors.toSet());

        log.debug("시스템 코드 목록 로드 완료: {}", validSysCodes);
    }

    /**
     * 헤더 메뉴 리스트를 모든 컨트롤러의 Model에 자동 추가
     * 사용자 역할 기반으로 조회하며, 캐싱 적용됨
     *
     * RequestContext 활용: 중복 조회 제거
     */
    @ModelAttribute("headerMenuList")
    public List<HeaderMenuVO> addHeaderMenu() {
        // 리소스성 요청은 메뉴 조회 불필요
        if (isResourceRequest()) {
            return Collections.emptyList();
        }

        try {
            // RequestContext에서 사용자 정보 조회 (세션 중복 조회 제거)
            LoginVO loginVO = RequestContext.getCurrentUser();

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

            log.trace("시스템 코드: sysCd={}, uri={}", sysCd, RequestContext.getRequestUri());

            if (loginVO != null) {
                userId = loginVO.getUserId();
                log.trace("로그인 사용자의 헤더 메뉴 조회: userId={}, sysCd={}", userId, sysCd);
            } else {
                // 로그인하지 않은 경우 guest 사용자로 처리
                userId = "guest";
                log.trace("비로그인 사용자 - guest 메뉴 조회");
                return Collections.emptyList();
            }

            // 유지보수 계정: 세션 역할 코드로 메뉴 조회
            List<HeaderMenuVO> menuList;
            if ("M".equals(loginVO.getUserStcd()) && loginVO.getUserRoles() != null) {
                List<String> roleCds = loginVO.getUserRoles().stream()
                        .map(UserRoleVO::getRoleCd)
                        .collect(Collectors.toList());
                menuList = menuService.getHeaderMenuByRole(sysCd, roleCds);
            } else {
                menuList = menuService.getHeaderMenuByUser(userId, sysCd);
            }

            log.trace("조회된 헤더 메뉴 개수: {} (userId={})", menuList.size(), userId);

            if (menuList.isEmpty()) {
                log.warn("조회된 메뉴가 없습니다. userId={}, sysCd={} - DB에 역할-메뉴 매핑 확인 필요", userId, sysCd);
            } else if (log.isTraceEnabled()) {
                log.trace("1-depth 메뉴 목록:");
                for (HeaderMenuVO menu : menuList) {
                    log.trace("  - {} (코드: {}, 하위메뉴: {}개)",
                        menu.getMenuNm(), menu.getMenuCd(),
                        menu.getChildren() != null ? menu.getChildren().size() : 0);
                }
            }

            return menuList;

        } catch (Exception e) {
            log.error("헤더 메뉴 조회 중 오류 발생", e);
            return Collections.emptyList();
        }
    }

    /**
     * 현재 활성 메뉴 코드를 Model에 자동 추가
     *
     * RequestContext 활용: MenuInterceptor에서 설정한 값 조회
     */
    @ModelAttribute("currentMenuCd")
    public String addCurrentMenuCd() {
        // 리소스성 요청은 메뉴 코드 조회 불필요
        if (isResourceRequest()) {
            return null;
        }

        // RequestContext에서 메뉴 코드 조회
        String menuCd = RequestContext.getCurrentMenuCd();
        log.trace("현재 메뉴 코드: {} (URI: {})", menuCd, RequestContext.getRequestUri());
        return menuCd;
    }

    /**
     * 현재 활성 메뉴명을 Model에 자동 추가
     */
    @ModelAttribute("currentMenuNm")
    public String addCurrentMenuNm() {
        String menuNm = RequestContext.getCurrentMenuNm();
        return menuNm != null ? menuNm : "";
    }

    /**
     * 현재 시스템 코드를 Model에 자동 추가
     */
    @ModelAttribute("currentSysCd")
    public String addCurrentSysCd() {
        String sysCd = RequestContext.getCurrentSysCd();
        return sysCd != null ? sysCd : "";
    }

    /**
     * 현재 시스템명을 Model에 자동 추가
     */
    @ModelAttribute("currentSysNm")
    public String addCurrentSysNm() {
        String sysNm = RequestContext.getCurrentSysNm();
        return sysNm != null ? sysNm : "";
    }

    /**
     * 로그인 사용자 정보를 Model에 자동 추가 (JSP에서 사용)
     *
     * RequestContext 활용: 세션 중복 조회 제거
     */
    @ModelAttribute("loginUser")
    public LoginVO addLoginUser() {
        return RequestContext.getCurrentUser();
    }

    /**
     * 리소스성 요청인지 확인 (메뉴 조회 불필요한 요청)
     *
     * RequestContextFilter.shouldNotFilter()에서 제외된 요청은
     * RequestContext가 설정되지 않아 uri가 null이 됨
     *
     * @return true: 리소스성 요청, false: 일반 요청
     */
    private boolean isResourceRequest() {
        return RequestContext.getRequestUri() == null;
    }
}