package gc.mda.kcg.permission.annotation;

import gc.mda.kcg.auth.AuthPrincipal;
import gc.mda.kcg.permission.PermissionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.JoinPoint;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @RequirePermission 어노테이션 처리 AOP.
 * 메서드 호출 직전 권한 체크 → 거부 시 AccessDeniedException 발생.
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class PermissionAspect {

    private final PermissionService permissionService;

    @Before("@annotation(gc.mda.kcg.permission.annotation.RequirePermission) || @within(gc.mda.kcg.permission.annotation.RequirePermission)")
    public void checkPermission(JoinPoint jp) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth == null || !(auth.getPrincipal() instanceof AuthPrincipal principal)) {
            throw new AuthenticationCredentialsNotFoundException("로그인이 필요합니다");
        }

        // 메서드 우선, 없으면 클래스
        MethodSignature ms = (MethodSignature) jp.getSignature();
        Method method = ms.getMethod();
        RequirePermission ann = method.getAnnotation(RequirePermission.class);
        if (ann == null) {
            ann = method.getDeclaringClass().getAnnotation(RequirePermission.class);
        }
        if (ann == null) return;

        boolean granted = permissionService.hasPermission(principal.getUserId(), ann.resource(), ann.operation());
        if (!granted) {
            log.warn("권한 거부: user={}, resource={}, op={}", principal.getUserAcnt(), ann.resource(), ann.operation());
            throw new AccessDeniedException("권한 없음: " + ann.resource() + "::" + ann.operation());
        }
    }
}
