package com.pressing.modules.auth;

import com.pressing.common.exception.ApiException;
import com.pressing.domain.menu.MenuItem;
import com.pressing.domain.menu.MenuItemRepository;
import com.pressing.domain.site.Site;
import com.pressing.domain.user.AppUser;
import com.pressing.domain.user.RoleCode;
import com.pressing.modules.auth.dto.MenuNodeDto;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MenuService {

    private final MenuItemRepository menuItemRepository;

    public MenuService(MenuItemRepository menuItemRepository) {
        this.menuItemRepository = menuItemRepository;
    }

    @Transactional(readOnly = true)
    public List<MenuNodeDto> menusFor(AppUser user, Site site) {
        Set<RoleCode> roles = user.getRoles().stream().map(r -> r.getCode()).collect(Collectors.toSet());
        boolean isAdmin = roles.contains(RoleCode.ADMIN);
        List<MenuItem> roots = menuItemRepository.findByParentIsNullAndActiveTrueOrderBySortOrderAsc();
        return roots.stream()
                .filter(item -> allowed(item, roles, isAdmin, site))
                .map(item -> toDto(item, roles, isAdmin, site))
                .filter(dto -> dto.path() != null || (dto.children() != null && !dto.children().isEmpty()))
                .toList();
    }

    private MenuNodeDto toDto(MenuItem item, Set<RoleCode> roles, boolean isAdmin, Site site) {
        List<MenuNodeDto> children = item.getChildren().stream()
                .filter(MenuItem::isActive)
                .filter(child -> allowed(child, roles, isAdmin, site))
                .sorted((a, b) -> Integer.compare(a.getSortOrder(), b.getSortOrder()))
                .map(child -> toDto(child, roles, isAdmin, site))
                .toList();
        return new MenuNodeDto(item.getId(), item.getLabel(), item.getPath(), item.getIcon(), item.getSortOrder(), children);
    }

    private boolean allowed(MenuItem item, Set<RoleCode> roles, boolean isAdmin, Site site) {
        if (item.getAllowedRoles() == null || item.getAllowedRoles().isEmpty() || isAdmin) {
            return true;
        }
        return item.getAllowedRoles().stream().anyMatch(roles::contains);
    }

    @Transactional(readOnly = true)
    public void assertPathAllowed(AppUser user, Site site, String path) {
        boolean ok = menusFor(user, site).stream().anyMatch(m -> containsPath(m, path));
        if (!ok) {
            throw ApiException.forbidden("Accès refusé à la ressource demandée");
        }
    }

    private boolean containsPath(MenuNodeDto node, String path) {
        if (path.equals(node.path())) {
            return true;
        }
        return node.children() != null && node.children().stream().anyMatch(c -> containsPath(c, path));
    }
}
