package com.zioinfo.kintex.common.audit;

import com.zioinfo.kintex.auth.KintexPrincipal;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

/**
 * {@link Audited} 부착 메서드를 감사 기록으로 감싼다(§7-3). 성공/실패를 기록하되 예외는 원형 그대로 전파한다.
 * 액터는 SecurityContext 의 {@link KintexPrincipal}에서 취득. <b>인자 값·응답 본문은 기록하지 않는다</b>(민감정보 차단).
 */
@Aspect
@Component
public class AuditAspect {

    private final AuditLogService auditLogService;

    public AuditAspect(AuditLogService auditLogService) {
        this.auditLogService = auditLogService;
    }

    @Around("@annotation(audited)")
    public Object audit(ProceedingJoinPoint pjp, Audited audited) throws Throwable {
        String actorId = null;
        String actorName = null;
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && auth.getPrincipal() instanceof KintexPrincipal p) {
            actorId = p.userId();
            actorName = p.displayName();
        }
        String method = ((MethodSignature) pjp.getSignature()).getMethod().getName();
        String targetType = audited.targetType().isBlank() ? null : audited.targetType();
        try {
            Object result = pjp.proceed();
            auditLogService.record(actorId, actorName, audited.action(), targetType,
                    null, null, "op=" + method, "SUCCESS");
            return result;
        } catch (Throwable ex) {
            // 실패 사유는 예외 클래스명만(스택트레이스·메시지 상세 미기록).
            auditLogService.record(actorId, actorName, audited.action(), targetType,
                    null, null, "op=" + method + " failed=" + ex.getClass().getSimpleName(), "FAILURE");
            throw ex;
        }
    }
}
