package fr.titionfire.ffsaf.rest;

import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.domain.service.MembreService;
import fr.titionfire.ffsaf.domain.service.PDFService;
import fr.titionfire.ffsaf.rest.data.MeData;
import fr.titionfire.ffsaf.rest.data.SimpleMembre;
import fr.titionfire.ffsaf.rest.exception.DForbiddenException;
import fr.titionfire.ffsaf.rest.from.UserSettingForm;
import fr.titionfire.ffsaf.utils.SecurityCtx;
import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.security.Authenticated;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
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 java.net.URISyntaxException;
import java.util.Objects;
import java.util.function.Consumer;

@Tag(name = "Membre", description = "Gestion des membres")
@Authenticated
@Path("api/member")
public class MembreEndpoints {

    @Inject
    MembreService membreService;

    @Inject
    PDFService pdfService;

    @ConfigProperty(name = "upload_dir")
    String media;

    @Inject
    SecurityCtx securityCtx;

    Consumer<MembreModel> checkPerm = Unchecked.consumer(membreModel -> {
        if (!securityCtx.roleHas("federation_admin") && !securityCtx.isInClubGroup(membreModel.getClub().getId()))
            throw new DForbiddenException();
    });

    @GET
    @Path("{id}")
    @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
    @Produces(MediaType.APPLICATION_JSON)
    @Operation(summary = "Renvoie les détails d'un membre en fonction de son identifiant", description = "Renvoie les " +
            "détails d'un membre en fonction de son identifiant")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "Les détails du membre"),
            @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<SimpleMembre> getById(
            @Parameter(description = "Identifiant de membre") @PathParam("id") long id) {
        return membreService.getById(id).onItem().invoke(checkPerm).map(SimpleMembre::fromModel);
    }

    @GET
    @Path("/find/licence")
    @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
    @Operation(summary = "Renvoie les détails d'un membre en fonction de son numéro de licence", description = "Renvoie " +
            "les détails d'un membre en fonction de son numéro de licence")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "Les détails du membre"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "404", description = "Le membre n'existe pas"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    @Produces(MediaType.APPLICATION_JSON)
    public Uni<SimpleMembre> getByLicence(@QueryParam("id") long id) {
        return membreService.getByLicence(id).onItem().invoke(checkPerm).map(SimpleMembre::fromModel);
    }

    @GET
    @Path("me")
    @Authenticated
    @Produces(MediaType.APPLICATION_JSON)
    @Operation(summary = "Renvoie les informations du membre connecté", description = "Renvoie les informations du " +
            "membre connecté, y compris le club et les licences")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "Les informations du membre connecté"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Uni<MeData> getMe() {
        return membreService.getMembre(securityCtx.getSubject());
    }

    @GET
    @Path("me/licence")
    @Authenticated
    @Produces(MediaType.APPLICATION_JSON)
    @Operation(summary = "Renvoie l'attestation d'adhesion du membre connecté", description = "Renvoie l'attestation d'adhesion du " +
            "membre connecté")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "L'attestation d'adhesion"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "404", description = "Le membre n'a pas de licence active"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Uni<Response> getMeLicence() {
        return pdfService.getLicencePdf(securityCtx.getSubject());
    }

    @PUT
    @Path("me/setting")
    @Authenticated
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.TEXT_PLAIN)
    @Operation(summary = "Met à jour les paramètres du membre connecté", description = "Met à jour les paramètres du membre connecté")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "Les paramètres ont été mis à jour"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Uni<Void> updateMeSettings(UserSettingForm settingForm) {
        return membreService.updateSettings(securityCtx.getSubject(), settingForm);
    }

    @GET
    @Path("me/photo")
    @Authenticated
    @Operation(summary = "Renvoie la photo du membre connecté", description = "Renvoie la photo du membre connecté")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "La photo"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Uni<Response> getMePhoto() {
        return membreService.getByAccountId(securityCtx.getSubject())
                .chain(Unchecked.function(
                        m -> Utils.getMediaFile(m.getId(), media, "ppMembre", Uni.createFrom().nullItem())));
    }

    @GET
    @Path("{id}/photo")
    @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
    @Operation(summary = "Renvoie la photo d'un membre", description = "Renvoie la photo d'un membre en fonction de son identifiant")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "La photo du membre"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "404", description = "Le membre n'existe pas ou n'a pas de photo"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Uni<Response> getPhoto(@PathParam("id") long id) throws URISyntaxException {
        return Utils.getMediaFile(id, media, "ppMembre", membreService.getById(id).onItem()
                .call(m -> Objects.equals(m.getUserId(), securityCtx.getSubject()) ?
                        Uni.createFrom().nullItem() : Uni.createFrom().item(m).invoke(checkPerm)));
    }

    @GET
    @Path("{id}/licence")
    @RolesAllowed({"federation_admin", "club_president", "club_secretaire", "club_tresorier", "club_respo_intra"})
    @Operation(summary = "Renvoie le pdf de la licence d'un membre", description = "Renvoie le pdf de la licence d'un membre en fonction de son identifiant")
    @APIResponses(value = {
            @APIResponse(responseCode = "200", description = "Le pdf de la licence"),
            @APIResponse(responseCode = "403", description = "Accès refusé"),
            @APIResponse(responseCode = "404", description = "Le membre n'existe pas ou n'a pas de licence active"),
            @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Uni<Response> getLicencePDF(@PathParam("id") long id) {
        return pdfService.getLicencePdf(membreService.getByIdWithLicence(id).onItem().invoke(checkPerm));
    }
}
