package fr.titionfire.ffsaf.rest;

import fr.titionfire.ffsaf.domain.service.KeycloakService;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.from.MemberPermForm;
import fr.titionfire.ffsaf.utils.Pair;
import fr.titionfire.ffsaf.utils.SecurityCtx;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.Vertx;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.keycloak.representations.idm.GroupRepresentation;

import java.util.ArrayList;
import java.util.List;

@Tag(name = "Compte", description = "Gestion des comptes utilisateurs")
@Path("api/compte")
public class CompteEndpoints {

    @Inject
    KeycloakService service;

    @Inject
    SecurityCtx securityCtx;

    @Inject
    Vertx vertx;

    @GET
    @Path("{id}")
    @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
    @Operation(summary = "Renvoie les informations d'un compte utilisateur", description = "Renvoie les informations d'un" +
            " compte utilisateur en fonction de son identifiant long (UUID)")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "Les informations du compte utilisateur"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "404", description = "Le compte utilisateur n'existe pas"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Uni<KeycloakService.UserCompteState> getCompte(@PathParam("id") String id) {
        return service.fetchCompte(id).call(pair -> vertx.getOrCreateContext().executeBlocking(() -> {
            if (!securityCtx.roleHas("federation_admin") && pair.getKey().groups().stream()
                    .map(GroupRepresentation::getPath)
                    .noneMatch(s -> s.startsWith("/club/") && securityCtx.contains(s)))
                throw new DForbiddenException();
            return pair;
        })).map(Pair::getValue);
    }

    @PUT
    @Path("{id}/init")
    @RolesAllowed("federation_admin")
    @Operation(summary = "Initialise un compte utilisateur", description = "Initialise un compte utilisateur en fonction" +
            " de son identifiant")
    @APIResponses(value = {
            @APIResponse(responseCode = "204", description = "Le compte utilisateur a été initialisé avec succès"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Uni<?> initCompte(@PathParam("id") long id) {
        return service.initCompte(id);
    }

    @PUT
    @Path("{id}/setUUID/{nid}")
    @RolesAllowed("federation_admin")
    @Operation(hidden = true)
    public Uni<?> initCompte(@PathParam("id") long id, @PathParam("nid") String nid) {
        return service.setId(id, nid);
    }

    @GET
    @Path("{id}/roles")
    @RolesAllowed("federation_admin")
    @Operation(summary = "Renvoie les rôles d'un compte utilisateur", description = "Renvoie les rôles d'un compte" +
            " utilisateur en fonction de son identifiant")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "Les rôles du compte utilisateur"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "404", description = "Le compte utilisateur n'existe pas"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Uni<List<String>> getRole(@PathParam("id") String id) {
        return service.fetchRole(id);
    }

    @PUT
    @Path("{id}/roles")
    @RolesAllowed("federation_admin")
    @Operation(summary = "Met à jour les rôles d'un compte utilisateur", description = "Met à jour les rôles d'un compte" +
            " utilisateur en fonction de son identifiant et des rôles fournis dans le formulaire")
    @APIResponses(value = {
            @APIResponse(responseCode = "204", description = "Les rôles du compte utilisateur ont été mis à jour avec succès"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "404", description = "Le compte utilisateur n'existe pas"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Uni<?> updateRole(@PathParam("id") String id, MemberPermForm form) {
        List<String> toAdd = new ArrayList<>();
        List<String> toRemove = new ArrayList<>();

        if (form.isFederation_admin()) toAdd.add("federation_admin");
        else toRemove.add("federation_admin");
        if (form.isSafca_super_admin()) toAdd.add("safca_super_admin");
        else toRemove.add("safca_super_admin");
        if (form.isSafca_user()) toAdd.add("safca_user");
        else toRemove.add("safca_user");
        if (form.isCreate_compet()) toAdd.add("create_compet");
        else toRemove.add("create_compet");

        return service.updateRole(id, toAdd, toRemove);
    }
}
