package fr.titionfire.ffsaf.domain.service; import fr.titionfire.ffsaf.data.model.MatchModel; import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.model.PouleModel; import fr.titionfire.ffsaf.data.model.TreeModel; import fr.titionfire.ffsaf.data.repository.*; import fr.titionfire.ffsaf.rest.data.PouleData; import fr.titionfire.ffsaf.rest.data.PouleFullData; import fr.titionfire.ffsaf.rest.data.TreeData; import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.utils.SecurityCtx; import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.hibernate.reactive.mutiny.Mutiny; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.stream.Stream; @WithSession @ApplicationScoped public class PouleService { @Inject PouleRepository repository; @Inject CompetitionRepository competRepository; @Inject MatchRepository matchRepository; @Inject TreeRepository treeRepository; @Inject CombRepository combRepository; @Inject CompetPermService permService; public Uni getById(SecurityCtx securityCtx, CompetitionSystem system, Long id) { return repository.find("systemId = ?1 AND system = ?2", id, system) .firstResult() .onItem().ifNull().failWith(() -> new RuntimeException("Poule not found")) .call(data -> permService.hasViewPerm(securityCtx, data.getCompet())) .map(PouleData::fromModel); } public Uni> getAll(SecurityCtx securityCtx, CompetitionSystem system) { return repository.list("system = ?1", system) .chain(o -> permService.getAllHaveAccess(securityCtx.getSubject()) .chain(map -> Uni.createFrom().item(o.stream() .filter(p -> { if (securityCtx.getSubject().equals(p.getCompet().getOwner())) return true; if (p.getSystem() == CompetitionSystem.SAFCA) { if (map.containsKey(p.getCompet().getId())) return map.get(p.getId()).equals("admin"); return securityCtx.roleHas("federation_admin") || securityCtx.roleHas("safca_super_admin"); } return securityCtx.roleHas("federation_admin"); }) .map(PouleData::fromModel).toList()) )); } public Uni addOrUpdate(SecurityCtx securityCtx, CompetitionSystem system, PouleData data) { return repository.find("systemId = ?1 AND system = ?2", data.getId(), system).firstResult() .chain(o -> { if (o == null) { return competRepository.findById(data.getCompet()) .onItem().ifNull().failWith(() -> new RuntimeException("Competition not found")) .call(o2 -> permService.hasEditPerm(securityCtx, o2)) .chain(competitionModel -> { PouleModel model = new PouleModel(); model.setId(null); model.setSystem(system); model.setSystemId(data.getId()); model.setCompet(competitionModel); model.setName(data.getName()); model.setMatchs(new ArrayList<>()); model.setTree(new ArrayList<>()); model.setType(data.getType()); return Panache.withTransaction(() -> repository.persist(model)); }); } else { o.setName(data.getName()); o.setType(data.getType()); return Panache.withTransaction(() -> repository.persist(o)); } }).map(PouleData::fromModel); } private MatchModel findMatch(List matchModelList, Long id) { return matchModelList.stream().filter(m -> m.getSystemId().equals(id)) .findFirst().orElse(null); } private TreeModel findNode(List node, Long match_id) { return node.stream().filter(m -> m.getMatch().getSystemId().equals(match_id)) .findFirst().orElse(null); } private void flatTreeChild(TreeModel current, ArrayList node) { if (current != null) { node.add(current); flatTreeChild(current.getLeft(), node); flatTreeChild(current.getRight(), node); } } private void flatTreeChild(TreeData current, ArrayList node) { if (current != null) { node.add(current); flatTreeChild(current.getLeft(), node); flatTreeChild(current.getRight(), node); } } private Uni persisteTree(TreeData data, List node, PouleModel poule, List matchModelList) { TreeModel mm = findNode(node, data.getMatch()); if (mm == null) { mm = new TreeModel(); mm.setId(null); } mm.setLevel(data.getLevel()); mm.setPoule(poule.getId()); mm.setMatch(findMatch(matchModelList, data.getMatch())); return Uni.createFrom().item(mm) .call(o -> (data.getLeft() == null ? Uni.createFrom().nullItem().invoke(o1 -> o.setLeft(null)) : persisteTree(data.getLeft(), node, poule, matchModelList).invoke(o::setLeft))) .call(o -> (data.getRight() == null ? Uni.createFrom().nullItem().invoke(o1 -> o.setRight(null)) : persisteTree(data.getRight(), node, poule, matchModelList).invoke(o::setRight))) .chain(o -> Panache.withTransaction(() -> treeRepository.persist(o))); } public Uni syncPoule(SecurityCtx securityCtx, CompetitionSystem system, PouleFullData data) { return repository.find("systemId = ?1 AND system = ?2", data.getId(), system) .firstResult() .onItem().ifNotNull().call(o2 -> permService.hasEditPerm(securityCtx, o2.getCompet())) .onItem().ifNull().switchTo( () -> competRepository.findById(data.getCompet()) .onItem().ifNull().failWith(() -> new RuntimeException("Compet not found")) .call(o -> permService.hasEditPerm(securityCtx, o)) .map(o -> { PouleModel model = new PouleModel(); model.setId(null); model.setSystem(system); model.setSystemId(data.getId()); model.setMatchs(new ArrayList<>()); model.setTree(new ArrayList<>()); model.setCompet(o); return model; })) .call(o -> Mutiny.fetch(o.getMatchs())) .call(o -> Mutiny.fetch(o.getTree())) .map(o -> { o.setName(data.getName()); o.setType(data.getType()); WorkData workData = new WorkData(); workData.poule = o; return workData; }) .call(o -> Panache.withTransaction(() -> repository.persist(o.poule))) .call(o -> (data.getMatches() == null || data.getMatches().isEmpty()) ? Uni.createFrom().nullItem() : Uni.createFrom() .item(data.getMatches().stream().flatMap(m -> Stream.of(m.getC1_id(), m.getC2_id()) .filter(Objects::nonNull)).distinct().toList()) .chain(ids -> ids.isEmpty() ? Uni.createFrom().nullItem() : combRepository.list("id IN ?1", ids) .invoke(o2 -> o2.forEach(m -> o.membres.put(m.getId(), m))) ) ) .invoke(in -> { ArrayList node = new ArrayList<>(); for (TreeModel treeModel : in.poule.getTree()) flatTreeChild(treeModel, node); ArrayList new_node = new ArrayList<>(); for (TreeData treeModel : data.getTrees()) flatTreeChild(treeModel, new_node); in.toRmNode = node.stream().filter(m -> new_node.stream() .noneMatch(m2 -> m2.getMatch().equals(m.getMatch().getSystemId()))) .map(TreeModel::getId).toList(); in.unlinkNode = node; in.unlinkNode.forEach(n -> { n.setRight(null); n.setLeft(null); }); in.toRmMatch = in.poule.getMatchs().stream() .filter(m -> data.getMatches().stream().noneMatch(m2 -> m2.getId().equals(m.getSystemId()))) .map(MatchModel::getId).toList(); }) .call(in -> in.unlinkNode.isEmpty() ? Uni.createFrom().nullItem() : Panache.withTransaction(() -> treeRepository.persist(in.unlinkNode))) .call(in -> in.toRmNode.isEmpty() ? Uni.createFrom().nullItem() : Panache.withTransaction(() -> treeRepository.delete("id IN ?1", in.toRmNode))) .call(in -> in.toRmMatch.isEmpty() ? Uni.createFrom().nullItem() : Panache.withTransaction(() -> Uni.join().all( in.toRmMatch.stream().map(l -> matchRepository.deleteById(l)).toList()) .andCollectFailures())) .call(in -> data.getMatches().isEmpty() ? Uni.createFrom().nullItem() : Uni.join().all( data.getMatches().stream().map(m -> { MatchModel mm = findMatch(in.poule.getMatchs(), m.getId()); if (mm == null) { mm = new MatchModel(); mm.setId(null); mm.setSystem(system); mm.setSystemId(m.getId()); } mm.setPoule(in.poule); mm.setPoule_ord(m.getPoule_ord()); mm.setC1_str(m.getC1_str()); mm.setC2_str(m.getC2_str()); mm.setC1_id(in.membres.getOrDefault(m.getC1_id(), null)); mm.setC2_id(in.membres.getOrDefault(m.getC2_id(), null)); mm.setEnd(m.isEnd()); mm.setGroupe(m.getGroupe()); mm.getScores().clear(); mm.getScores().addAll(m.getScores()); MatchModel finalMm = mm; return Panache.withTransaction(() -> matchRepository.persist(finalMm) .invoke(o -> in.match.add(o))); }).toList()) .andCollectFailures()) .call(in -> data.getTrees().isEmpty() ? Uni.createFrom().nullItem() : Uni.join().all(data.getTrees().stream() .map(m -> persisteTree(m, in.poule.getTree(), in.poule, in.match)).toList()) .andCollectFailures()) .map(__ -> "OK"); } private static class WorkData { PouleModel poule; HashMap membres = new HashMap<>(); List match = new ArrayList<>(); List toRmMatch; List unlinkNode; List toRmNode; } public Uni delete(SecurityCtx securityCtx, CompetitionSystem system, Long id) { return repository.find("systemId = ?1 AND system = ?2", id, system).firstResult() .onItem().ifNull().failWith(() -> new RuntimeException("Poule not found")) .call(o -> permService.hasEditPerm(securityCtx, o.getCompet())) .call(o -> Mutiny.fetch(o.getMatchs())) .call(o -> Mutiny.fetch(o.getTree()) .call(o2 -> o2.isEmpty() ? Uni.createFrom().nullItem() : Uni.createFrom().item(o2.stream().peek(m -> { m.setRight(null); m.setLeft(null); }).toList()) .call(in -> Panache.withTransaction(() -> treeRepository.persist(in))) .map(in -> in.stream().map(TreeModel::getId).toList()) .call(in -> in.isEmpty() ? Uni.createFrom().nullItem() : Panache.withTransaction(() -> treeRepository.delete("id IN ?1", in))) ) ) .call(o -> o.getMatchs().isEmpty() ? Uni.createFrom().nullItem() : Panache.withTransaction(() -> Uni.join().all( o.getMatchs().stream().map(l -> matchRepository.deleteById(l.getId())).toList()) .andCollectFailures())) .chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId()))); } }