package com.takensoft.common.service;

import com.takensoft.cms.mber.vo.MberVO;
import com.takensoft.common.exception.CustomAccessDeniedException;
import com.takensoft.common.util.JWTUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.Cookie;

/**
 * @author takensoft
 * @since 2025.01.22
 * @modification
 *     since    |    author    | description
 *  2025.01.22  |  takensoft   | 최초 등록
 *
 * 사용자 검증 서비스
 */
@Service("authorizationService")
@RequiredArgsConstructor
public class VerificationService {

    private final JWTUtil jwtUtil;


    /**
     * @return 현재 인증된 사용자 정보
     * @throws CustomAccessDeniedException 인증되지 않은 경우 예외 발생
     *
     * 현재 로그인된 사용자를 가져오는 공통 메서드
     */
    private MberVO getAuthenticatedUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(authentication == null || !authentication.isAuthenticated()) {
            throw new CustomAccessDeniedException("접근 권한이 없습니다.");
        }

        Object principal = authentication.getPrincipal();
        if(!(principal instanceof MberVO)) {
            throw new CustomAccessDeniedException("접근 권한이 없습니다.");
        }
        return (MberVO) principal;
    }

    /**
     * @param targetUserId 접근하려는 사용자 ID
     * @throws CustomAccessDeniedException - 접근 권한이 없을 경우 예외 발생
     *
     * 특정 사용자 ID에 대한 접근 권한 검증
     */
    public void verifyAccess(String targetUserId) {
        MberVO user = getAuthenticatedUser();
        // 관리자 권한 여부
        boolean isAdmin = user.getAuthorList().stream()
                .anyMatch(auth -> "ROLE_ADMIN".equals(auth.getAuthrtCd()));
        // 본인 여부
        boolean isOwner = user.getMbrId().equals(targetUserId);

        if(!isAdmin && !isOwner) {
            throw new CustomAccessDeniedException("접근 권한이 없습니다.");
        }
    }

    /**
     * @return 관리자 여부(true, false)
     *
     * 관리자 여부 검증
     */
    public boolean verifyAdmin() {
        MberVO user = getAuthenticatedUser();
        // 관리자 권한 여부 반환
        return user.getAuthorList().stream()
                .anyMatch(auth -> "ROLE_ADMIN".equals(auth.getAuthrtCd()));
    }

    /**
     * @return 현재 로그인된 사용자 ID
     *
     * 로그인된 사용자 아이디 반환
     *  - 등록자, 수정자 입력 시 사용
     */
    public String getCurrentUserId() {
        String userId = null;

        try {
            // 1. SecurityContext에서 시도
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

            if(authentication != null && authentication.isAuthenticated()) {
                Object principal = authentication.getPrincipal();
                if(principal instanceof MberVO) {
                    userId = ((MberVO) principal).getMbrId();
                }
            }

            // 2. SecurityContext에서 조회 실패시 세션에서 직접 조회
            if (userId == null) {
                try {
                    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
                    HttpServletRequest request = attributes.getRequest();
                    HttpSession session = request.getSession(false);

                    if (session != null) {
                        userId = (String) session.getAttribute("mbrId");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            // 3. JWT 토큰에서 조회 시도 (JWT 모드인 경우)
            if (userId == null) {
                try {
                    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
                    HttpServletRequest request = attributes.getRequest();

                    // Authorization 헤더에서 토큰 추출
                    String authHeader = request.getHeader("Authorization");
                    String token = null;

                    if (authHeader != null && authHeader.startsWith("Bearer ")) {
                        token = authHeader.substring(7);
                    } else if (request.getCookies() != null) {
                        // 쿠키에서 토큰 추출
                        for (Cookie cookie : request.getCookies()) {
                            if ("Authorization".equals(cookie.getName()) || "refresh".equals(cookie.getName())) {
                                token = cookie.getValue();
                                if (token.startsWith("Bearer ")) {
                                    token = token.substring(7);
                                }
                                break;
                            }
                        }
                    }
                    if (token != null && jwtUtil != null) {
                        userId = (String) jwtUtil.getClaim(token, "mbrId");
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return userId;
    }
}
