package de.cannamanage.api.security;

import de.cannamanage.domain.entity.StaffAccount;
import de.cannamanage.domain.enums.StaffPermission;
import de.cannamanage.service.repository.StaffAccountRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.UUID;

/**
 * SpEL-accessible bean for fine-grained staff permission checks.
 * Usage in @PreAuthorize:
 *   @PreAuthorize("hasRole('ADMIN') or @staffPermissions.has(authentication, T(de.cannamanage.domain.enums.StaffPermission).RECORD_DISTRIBUTION)")
 */
@Slf4j
@Component("staffPermissions")
@RequiredArgsConstructor
public class StaffPermissionChecker {

    private final StaffAccountRepository staffAccountRepository;

    /**
     * Checks if the authenticated user has the required permission.
     * ADMIN role always passes. STAFF checks granted_permissions on their StaffAccount.
     */
    public boolean has(Authentication authentication, StaffPermission required) {
        if (authentication == null || !authentication.isAuthenticated()) {
            return false;
        }

        // ADMIN always has all permissions
        boolean isAdmin = authentication.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .anyMatch(a -> a.equals("ROLE_ADMIN"));
        if (isAdmin) {
            return true;
        }

        // STAFF must have the specific permission granted
        boolean isStaff = authentication.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .anyMatch(a -> a.equals("ROLE_STAFF"));
        if (!isStaff) {
            return false;
        }

        UUID userId = (UUID) authentication.getPrincipal();
        return staffAccountRepository.findByUserId(userId)
                .filter(StaffAccount::isActive)
                .map(staff -> staff.hasPermission(required))
                .orElse(false);
    }
}
