package gc.mda.kcg.permission;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.UUID;

/**
 * 사용자 권한 조회/캐싱 서비스.
 * 권한 변경 시 CacheEvict로 무효화.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PermissionService {

    private final UserRoleRepository userRoleRepository;
    private final PermRepository permRepository;
    private final PermTreeRepository permTreeRepository;
    private final RoleRepository roleRepository;
    private final PermResolver permResolver;

    /**
     * 사용자 ID로 해석된 권한 맵 조회.
     * Caffeine 캐시 (TTL 10분, 권한 변경 시 evict).
     */
    @Cacheable(value = "permissions", key = "#userId")
    @Transactional(readOnly = true)
    public Map<String, List<String>> getResolvedPermissionsByUserId(UUID userId) {
        // 1. 사용자의 역할 조회
        List<UserRole> userRoles = userRoleRepository.findByUserId(userId);
        if (userRoles.isEmpty()) {
            log.debug("User {} has no roles", userId);
            return Collections.emptyMap();
        }

        List<Long> roleSns = userRoles.stream().map(UserRole::getRoleSn).toList();

        // 2. 역할별 명시 권한 로드
        List<Perm> perms = permRepository.findByRoleSnIn(roleSns);
        Map<Long, Map<String, String>> permsByRole = new HashMap<>();
        for (Perm p : perms) {
            permsByRole
                .computeIfAbsent(p.getRoleSn(), k -> new HashMap<>())
                .put(PermResolver.makePermKey(p.getRsrcCd(), p.getOperCd()), p.getGrantYn());
        }
        // 권한이 하나도 없는 역할도 빈 맵으로 등록 (트리 순회는 필요)
        for (Long sn : roleSns) {
            permsByRole.computeIfAbsent(sn, k -> new HashMap<>());
        }

        // 3. 트리 노드 로드 (use_yn=Y)
        List<PermTree> treeNodes = permTreeRepository.findByUseYn("Y");

        // 4. 다중 역할 해석
        Map<String, List<String>> resolved = permResolver.resolveMultiRole(treeNodes, permsByRole);
        log.debug("Resolved {} resources for user {}", resolved.size(), userId);
        return resolved;
    }

    /**
     * 권한 체크 (resource + operation).
     */
    public boolean hasPermission(UUID userId, String rsrcCd, String operCd) {
        Map<String, List<String>> resolved = getResolvedPermissionsByUserId(userId);
        return permResolver.hasPermission(resolved, rsrcCd, operCd);
    }

    /**
     * 사용자 역할 코드 목록 조회.
     */
    @Transactional(readOnly = true)
    public List<String> getRoleCodesByUserId(UUID userId) {
        return userRoleRepository.findRoleCodesByUserId(userId);
    }

    /**
     * 권한 캐시 무효화 (역할 배정/권한 매트릭스 변경 시 호출).
     */
    @CacheEvict(value = "permissions", key = "#userId")
    public void evictUserPermissions(UUID userId) {
        log.info("Evicted permissions cache for user {}", userId);
    }

    /**
     * 전체 권한 캐시 무효화 (대량 변경 시).
     */
    @CacheEvict(value = "permissions", allEntries = true)
    public void evictAllPermissions() {
        log.info("Evicted all permissions cache");
    }
}
