package fr.titionfire.ffsaf.domain.service; import fr.titionfire.ffsaf.data.model.CompetitionModel; import fr.titionfire.ffsaf.data.model.MembreModel; import fr.titionfire.ffsaf.data.repository.*; import fr.titionfire.ffsaf.net2.ServerCustom; import fr.titionfire.ffsaf.net2.data.SimpleCompet; import fr.titionfire.ffsaf.net2.request.SReqCompet; import fr.titionfire.ffsaf.net2.request.SReqRegister; import fr.titionfire.ffsaf.rest.data.CompetitionData; import fr.titionfire.ffsaf.rest.data.RegisterRequestData; import fr.titionfire.ffsaf.rest.data.SimpleCompetData; import fr.titionfire.ffsaf.rest.data.SimpleRegisterComb; import fr.titionfire.ffsaf.rest.exception.DBadRequestException; import fr.titionfire.ffsaf.rest.exception.DForbiddenException; import fr.titionfire.ffsaf.rest.exception.DNotFoundException; import fr.titionfire.ffsaf.utils.CompetitionSystem; import fr.titionfire.ffsaf.data.model.RegisterModel; import fr.titionfire.ffsaf.utils.SecurityCtx; import fr.titionfire.ffsaf.utils.Utils; import io.quarkus.cache.Cache; import io.quarkus.cache.CacheName; import io.quarkus.hibernate.reactive.panache.Panache; import io.quarkus.hibernate.reactive.panache.common.WithSession; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.unchecked.Unchecked; import io.vertx.mutiny.core.Vertx; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.hibernate.reactive.mutiny.Mutiny; import org.keycloak.representations.idm.UserRepresentation; import java.util.*; import java.util.stream.Stream; @WithSession @ApplicationScoped public class CompetitionService { @Inject CompetitionRepository repository; @Inject PouleRepository pouleRepository; @Inject MatchRepository matchRepository; @Inject KeycloakService keycloakService; @Inject CombRepository combRepository; @Inject ServerCustom serverCustom; @Inject CompetPermService permService; @Inject Vertx vertx; @Inject @CacheName("safca-config") Cache cache; @Inject @CacheName("safca-have-access") Cache cacheAccess; @Inject RegisterRepository registerRepository; public Uni getById(SecurityCtx securityCtx, Long id) { if (id == 0) { return Uni.createFrom() .item(new CompetitionData(null, "", "", new Date(), CompetitionSystem.SAFCA, null, "", "", null)); } return permService.hasViewPerm(securityCtx, id) .chain(competitionModel -> Mutiny.fetch(competitionModel.getInsc()) .map(insc -> CompetitionData.fromModel(competitionModel).addInsc(insc))) .chain(data -> vertx.getOrCreateContext().executeBlocking(() -> { keycloakService.getUser(UUID.fromString(data.getOwner())) .ifPresent(user -> data.setOwner(user.getUsername())); return data; }) ); } public Uni> getAll(SecurityCtx securityCtx) { return repository.listAll() .chain(o -> permService.getAllHaveAccess(securityCtx.getSubject()) .chain(map -> Uni.createFrom().item(o.stream() .filter(p -> { if (securityCtx.getSubject().equals(p.getOwner())) return true; if (p.getSystem() == CompetitionSystem.SAFCA) { if (map.containsKey(p.getId())) return map.get(p.getId()).equals("admin"); return securityCtx.roleHas("federation_admin") || securityCtx.roleHas("safca_super_admin"); } return securityCtx.roleHas("federation_admin"); }) .map(CompetitionData::fromModel).toList()) )); } public Uni> getAllSystem(SecurityCtx securityCtx, CompetitionSystem system) { if (system == CompetitionSystem.SAFCA) { return permService.getAllHaveAccess(securityCtx.getSubject()) .chain(map -> repository.list("system = ?1", system) .map(data -> data.stream() .filter(p -> { if (securityCtx.getSubject().equals(p.getOwner())) return true; if (map.containsKey(p.getId())) return map.get(p.getId()).equals("admin"); return securityCtx.roleHas("federation_admin") || securityCtx.roleHas("safca_super_admin"); }) .map(CompetitionData::fromModel).toList()) ); } return repository.list("system = ?1", system) .map(data -> data.stream() .filter(p -> { if (securityCtx.getSubject().equals(p.getOwner())) return true; return securityCtx.roleHas("federation_admin") || securityCtx.isInClubGroup(p.getClub().getId()); }) .map(CompetitionData::fromModel).toList()); } public Uni addOrUpdate(SecurityCtx securityCtx, CompetitionData data) { if (data.getId() == null) { return combRepository.find("userId = ?1", securityCtx.getSubject()).firstResult() .invoke(Unchecked.consumer(combModel -> { if (combModel == null) throw new DNotFoundException("Profile non trouvé"); if (data.getSystem() == CompetitionSystem.SAFCA) if (!securityCtx.getRoles().contains("safca_create_compet")) throw new DForbiddenException("Vous ne pouvez pas créer de compétition SAFCA"); })) .map(MembreModel::getClub) .chain(clubModel -> { CompetitionModel model = new CompetitionModel(); model.setId(null); model.setSystem(data.getSystem()); model.setClub(clubModel); model.setDate(data.getDate()); model.setInsc(new ArrayList<>()); model.setUuid(UUID.randomUUID().toString()); model.setName(data.getName()); model.setOwner(securityCtx.getSubject()); return Panache.withTransaction(() -> repository.persist(model)); }).map(CompetitionData::fromModel) .call(__ -> cacheAccess.invalidate(securityCtx.getSubject())); } else { return permService.hasEditPerm(securityCtx, data.getId()) .chain(model -> { model.setDate(data.getDate()); model.setName(data.getName()); return vertx.getOrCreateContext().executeBlocking(() -> keycloakService.getUser(data.getOwner()).map(UserRepresentation::getId).orElse(null)) .invoke(Unchecked.consumer(newOwner -> { if (newOwner == null) throw new DBadRequestException("User " + data.getOwner() + " not found"); if (!newOwner.equals(model.getOwner())) { if (!securityCtx.roleHas("federation_admin") && !securityCtx.roleHas("safca_super_admin") && !securityCtx.getSubject().equals(model.getOwner())) throw new DForbiddenException(); model.setOwner(newOwner); } })) .chain(__ -> Panache.withTransaction(() -> repository.persist(model))); }).map(CompetitionData::fromModel) .call(__ -> cacheAccess.invalidate(securityCtx.getSubject())); } } public Uni> getRegister(SecurityCtx securityCtx, Long id) { return permService.hasEditPerm(securityCtx, id) .chain(c -> Mutiny.fetch(c.getInsc())) .onItem().transformToMulti(Multi.createFrom()::iterable) .onItem().call(combModel -> Mutiny.fetch(combModel.getMembre().getLicences())) .map(combModel -> SimpleRegisterComb.fromModel(combModel, combModel.getMembre().getLicences())) .collect().asList(); } public Uni addRegisterComb(SecurityCtx securityCtx, Long id, RegisterRequestData data) { return permService.hasEditPerm(securityCtx, id) .chain(c -> findComb(data.getLicence(), data.getFname(), data.getLname()) .chain(combModel -> Mutiny.fetch(c.getInsc()) .chain(Unchecked.function(insc -> { Optional opt = insc.stream() .filter(m -> m.getMembre().equals(combModel)).findAny(); RegisterModel r; if (opt.isPresent()) { r = opt.get(); r.setWeight(data.getWeight()); r.setOverCategory(data.getOverCategory()); r.setCategorie( (combModel.getBirth_date() == null) ? combModel.getCategorie() : Utils.getCategoryFormBirthDate(combModel.getBirth_date(), c.getDate())); int days = Utils.getDaysBeforeCompetition(c.getDate()); if (days > -7) { r.setClub(combModel.getClub()); } } else { r = new RegisterModel(c ,combModel, data.getWeight(), data.getOverCategory(), (combModel.getBirth_date() == null) ? combModel.getCategorie() : Utils.getCategoryFormBirthDate(combModel.getBirth_date(), c.getDate()), (combModel.getClub() == null) ? null : combModel.getClub()); insc.add(r); } if (c.getSystem() == CompetitionSystem.SAFCA) { SReqRegister.sendIfNeed(serverCustom.clients, new CompetitionData.SimpleRegister(r.getMembre().getId(), r.getOverCategory(), r.getWeight(), r.getCategorie(), (r.getClub() == null) ? null : r.getClub().getId()), c.getId()); } return Panache.withTransaction(() -> repository.persist(c)).map(__ -> r); })))) .chain(r -> Mutiny.fetch(r.getMembre().getLicences()) .map(licences -> SimpleRegisterComb.fromModel(r, licences))); } private Uni findComb(Long licence, String fname, String lname) { if (licence != null && licence != 0) { return combRepository.find("licence = ?1", licence).firstResult() .invoke(Unchecked.consumer(combModel -> { if (combModel == null) throw new DForbiddenException("Licence " + licence + " non trouvé"); })); } else { if (fname == null || lname == null) return Uni.createFrom().failure(new DBadRequestException("Nom et prénom requis")); return combRepository.find("LOWER(lname) LIKE LOWER(?1) OR LOWER(fname) LIKE LOWER(?2)", lname, fname).firstResult() .invoke(Unchecked.consumer(combModel -> { if (combModel == null) throw new DForbiddenException("Combattant " + fname + " " + lname + " non trouvé"); })); } } public Uni removeRegisterComb(SecurityCtx securityCtx, Long id, Long combId) { return permService.hasEditPerm(securityCtx, id) .chain(c -> registerRepository.delete("competition = ?1 AND membre.id = ?2", c, combId) .invoke(Unchecked.consumer(l -> { if (l != 0){ if (c.getSystem() == CompetitionSystem.SAFCA) { SReqRegister.sendRmIfNeed(serverCustom.clients, combId, id); } }else{ throw new DBadRequestException("Combattant non inscrit"); } })) ).replaceWithVoid(); } public Uni delete(SecurityCtx securityCtx, Long id) { return repository.findById(id).invoke(Unchecked.consumer(c -> { if (!(securityCtx.getSubject().equals(c.getOwner()) || securityCtx.roleHas("federation_admin"))) throw new DForbiddenException(); })) .call(competitionModel -> pouleRepository.list("compet = ?1", competitionModel) .call(pouleModels -> pouleModels.isEmpty() ? Uni.createFrom().nullItem() : Uni.join().all(pouleModels.stream() .map(pouleModel -> Panache.withTransaction( () -> matchRepository.delete("poule = ?1", pouleModel.getId()))) .toList()) .andCollectFailures())) .call(competitionModel -> Panache.withTransaction( () -> pouleRepository.delete("compet = ?1", competitionModel))) .chain(model -> Panache.withTransaction(() -> repository.delete("id", model.getId()))) .invoke(o -> SReqCompet.rmCompet(serverCustom.clients, id)) .call(__ -> cache.invalidate(id)); } public Uni getSafcaData(SecurityCtx securityCtx, Long id) { return permService.getSafcaConfig(id) .call(Unchecked.function(o -> { if (!securityCtx.getSubject().equals(o.owner()) && !securityCtx.roleHas("federation_admin") && !securityCtx.roleHas("safca_super_admin") && !o.admin().contains(UUID.fromString(securityCtx.getSubject())) && !o.table().contains(UUID.fromString(securityCtx.getSubject()))) throw new DForbiddenException(); return Uni.createFrom().nullItem(); })) .chain(simpleCompet -> { SimpleCompetData data = SimpleCompetData.fromModel(simpleCompet); return vertx.getOrCreateContext().executeBlocking(() -> { data.setAdmin(simpleCompet.admin().stream().map(uuid -> keycloakService.getUser(uuid)) .filter(Optional::isPresent) .map(user -> user.get().getUsername()) .toList()); data.setTable(simpleCompet.table().stream().map(uuid -> keycloakService.getUser(uuid)) .filter(Optional::isPresent) .map(user -> user.get().getUsername()) .toList()); return data; }); }); } public Uni setSafcaData(SecurityCtx securityCtx, SimpleCompetData data) { return permService.hasEditPerm(securityCtx, data.getId()) .chain(__ -> vertx.getOrCreateContext().executeBlocking(() -> { ArrayList admin = new ArrayList<>(); ArrayList table = new ArrayList<>(); for (String username : data.getAdmin()) { Optional opt = keycloakService.getUser(username); if (opt.isEmpty()) throw new DBadRequestException("User " + username + " not found"); admin.add(UUID.fromString(opt.get().getId())); } for (String username : data.getTable()) { Optional opt = keycloakService.getUser(username); if (opt.isEmpty()) throw new DBadRequestException("User " + username + " not found"); table.add(UUID.fromString(opt.get().getId())); } return new SimpleCompet(data.getId(), "", data.isShow_blason(), data.isShow_flag(), admin, table); })) .invoke(simpleCompet -> SReqCompet.sendUpdate(serverCustom.clients, simpleCompet)) .call(simpleCompet -> permService.getSafcaConfig(data.getId()) .call(c -> { List> list = Stream.concat( Stream.concat( c.admin().stream().filter(uuid -> !simpleCompet.admin().contains(uuid)), simpleCompet.admin().stream().filter(uuid -> !c.admin().contains(uuid))), Stream.concat( c.table().stream().filter(uuid -> !simpleCompet.table().contains(uuid)), simpleCompet.table().stream().filter(uuid -> !c.table().contains(uuid))) ).map(uuid -> cacheAccess.invalidate(uuid.toString())).toList(); if (list.isEmpty()) return Uni.createFrom().nullItem(); return Uni.join().all(list).andCollectFailures(); })) .call(__ -> cache.invalidate(data.getId())); } }