package org.raddatz.familienarchiv.security;

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.raddatz.familienarchiv.exception.DomainException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class PermissionAspect {

    // Triggers if the Method OR the Class has the annotation
    @Around("@annotation(org.raddatz.familienarchiv.security.RequirePermission) || @within(org.raddatz.familienarchiv.security.RequirePermission)")
    public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {

        // 1. Find the annotation (Priority: Method > Class)
        RequirePermission permission = getAnnotation(joinPoint);

        if (permission != null) {
            validateUserAccess(permission.value()); // value() is now Permission[]
        }

        return joinPoint.proceed();
    }

    private RequirePermission getAnnotation(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // Check Method Level
        RequirePermission methodAnnotation = method.getAnnotation(RequirePermission.class);
        if (methodAnnotation != null) {
            return methodAnnotation;
        }

        // Check Class Level
        return joinPoint.getTarget().getClass().getAnnotation(RequirePermission.class);
    }

    private void validateUserAccess(Permission[] requiredPerms) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (auth == null || !auth.isAuthenticated()) {
            throw DomainException.unauthorized("Not authenticated");
        }

        boolean hasAny = auth.getAuthorities().stream()
                .anyMatch(a -> {
                    for (Permission p : requiredPerms) {
                        if (a.getAuthority().equals(p.name())) return true;
                    }
                    return false;
                });

        if (!hasAny) {
            throw DomainException.forbidden("Missing required permission");
        }
    }
}
