package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.api.dto.reference.request.CreateTypeReferenceRequest;
import dev.lions.unionflow.server.api.dto.reference.request.UpdateTypeReferenceRequest;
import dev.lions.unionflow.server.api.dto.reference.response.TypeReferenceResponse;
import dev.lions.unionflow.server.service.TypeReferenceService;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
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 org.jboss.logging.Logger;

/**
 * Ressource REST pour le CRUD des données
 * de référence.
 *
 * <p>
 * Expose les endpoints permettant de gérer
 * dynamiquement toutes les valeurs catégorielles
 * de l'application (statuts, types, devises,
 * priorités, etc.) via la table
 * {@code types_reference}.
 *
 * @author UnionFlow Team
 * @version 3.0
 * @since 2026-02-21
 */
@Path("/api/v1/types-reference")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Types de référence", description = "Gestion des données de référence"
        + " paramétrables")
@RolesAllowed({
        "SUPER_ADMIN",
        "ADMIN", "MEMBRE", "USER"
})
public class TypeReferenceResource {

    private static final Logger LOG = Logger.getLogger(TypeReferenceResource.class);

    @Inject
    TypeReferenceService service;

    /**
     * Liste les références actives d'un domaine.
     *
     * @param domaine        le domaine fonctionnel
     * @param organisationId l'UUID de l'organisation
     * @return liste triée par ordre d'affichage
     */
    @GET
    @Operation(summary = "Lister par domaine", description = "Récupère les valeurs actives"
            + " d'un domaine donné")
    @APIResponses({
            @APIResponse(responseCode = "200", description = "Liste récupérée", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = TypeReferenceResponse.class))),
            @APIResponse(responseCode = "401", description = "Non authentifié")
    })
    public Response listerParDomaine(
            @Parameter(description = "Domaine fonctionnel", example = "STATUT_ORGANISATION", required = true) @QueryParam("domaine") String domaine,
            @Parameter(description = "UUID de l'organisation", example = "550e8400-e29b-41d4-a716-4466"
                    + "55440000") @QueryParam("organisationId") UUID organisationId) {
        if (domaine == null || domaine.isBlank()) {
            return Response
                    .status(Response.Status.BAD_REQUEST)
                    .entity(Map.of(
                            "error",
                            "Le paramètre 'domaine' est"
                                    + " obligatoire"))
                    .build();
        }
        List<TypeReferenceResponse> result = service.listerParDomaine(
                domaine, organisationId);
        return Response.ok(result).build();
    }

    /**
     * Retourne un type de référence par son ID.
     *
     * @param id l'UUID du type de référence
     * @return le détail complet
     */
    @GET
    @Path("/{id}")
    @Operation(summary = "Détail d'une référence", description = "Récupère une référence par"
            + " son identifiant")
    @APIResponses({
            @APIResponse(responseCode = "200", description = "Référence trouvée"),
            @APIResponse(responseCode = "404", description = "Référence non trouvée")
    })
    public Response trouverParId(
            @PathParam("id") UUID id) {
        try {
            TypeReferenceResponse response = service.trouverParId(id);
            return Response.ok(response).build();
        } catch (IllegalArgumentException e) {
            return Response
                    .status(Response.Status.NOT_FOUND)
                    .entity(Map.of("error", e.getMessage()))
                    .build();
        }
    }

    /**
     * Liste les domaines disponibles.
     *
     * @return noms de domaines distincts
     */
    @GET
    @Path("/domaines")
    @Operation(summary = "Lister les domaines", description = "Récupère la liste des domaines"
            + " disponibles")
    public Response listerDomaines() {
        List<String> domaines = service.listerDomaines();
        return Response.ok(domaines).build();
    }

    /**
     * Retourne la valeur par défaut d'un domaine.
     *
     * @param domaine        le domaine fonctionnel
     * @param organisationId l'UUID de l'organisation
     * @return la valeur par défaut
     */
    @GET
    @Path("/defaut")
    @Operation(summary = "Valeur par défaut", description = "Récupère la valeur par défaut"
            + " d'un domaine")
    public Response trouverDefaut(
            @Parameter(description = "Domaine fonctionnel", required = true) @QueryParam("domaine") String domaine,
            @Parameter(description = "UUID de l'organisation") @QueryParam("organisationId") UUID organisationId) {
        if (domaine == null || domaine.isBlank()) {
            return Response.status(Response.Status.BAD_REQUEST)
                    .entity(Map.of("error", "Le paramètre 'domaine' est obligatoire"))
                    .build();
        }
        try {
            TypeReferenceResponse response = service.trouverDefaut(
                    domaine, organisationId);
            return Response.ok(response).build();
        } catch (IllegalArgumentException e) {
            return Response
                    .status(Response.Status.NOT_FOUND)
                    .entity(Map.of("error", e.getMessage()))
                    .build();
        }
    }

    /**
     * Crée une nouvelle donnée de référence.
     *
     * @param request la requête de création validée
     * @return la référence créée (HTTP 201)
     */
    @POST
    @RolesAllowed({
            "SUPER_ADMIN", "ADMIN"
    })
    @Operation(summary = "Créer une référence", description = "Ajoute une nouvelle valeur dans"
            + " un domaine")
    @APIResponses({
            @APIResponse(responseCode = "201", description = "Référence créée"),
            @APIResponse(responseCode = "400", description = "Données invalides ou"
                    + " code dupliqué")
    })
    public Response creer(
            @Valid CreateTypeReferenceRequest request) {
        try {
            TypeReferenceResponse created = service.creer(request);
            return Response
                    .status(Response.Status.CREATED)
                    .entity(created)
                    .build();
        } catch (IllegalArgumentException e) {
            LOG.warnf(
                    "Erreur création référence: %s",
                    e.getMessage());
            return Response
                    .status(Response.Status.BAD_REQUEST)
                    .entity(Map.of("error", e.getMessage()))
                    .build();
        }
    }

    /**
     * Met à jour une donnée de référence.
     *
     * @param id      l'UUID de la référence
     * @param request la requête de mise à jour
     * @return la référence mise à jour
     */
    @PUT
    @Path("/{id}")
    @RolesAllowed({
            "SUPER_ADMIN", "ADMIN"
    })
    @Operation(summary = "Modifier une référence", description = "Met à jour une valeur"
            + " existante")
    @APIResponses({
            @APIResponse(responseCode = "200", description = "Référence modifiée"),
            @APIResponse(responseCode = "400", description = "Données invalides"),
            @APIResponse(responseCode = "404", description = "Référence non trouvée")
    })
    public Response modifier(
            @PathParam("id") UUID id,
            @Valid UpdateTypeReferenceRequest request) {
        try {
            TypeReferenceResponse updated = service.modifier(id, request);
            return Response.ok(updated).build();
        } catch (IllegalArgumentException e) {
            LOG.warnf(
                    "Erreur modification référence: %s",
                    e.getMessage());
            return Response
                    .status(Response.Status.BAD_REQUEST)
                    .entity(Map.of("error", e.getMessage()))
                    .build();
        }
    }

    /**
     * Supprime une donnée de référence.
     *
     * <p>
     * Les valeurs système ne peuvent pas être
     * supprimées.
     *
     * @param id l'UUID de la référence
     * @return HTTP 204 si succès
     */
    @DELETE
    @Path("/{id}")
    @RolesAllowed({
            "SUPER_ADMIN"
    })
    @Operation(summary = "Supprimer une référence", description = "Supprime une valeur non"
            + " système")
    @APIResponses({
            @APIResponse(responseCode = "204", description = "Référence supprimée"),
            @APIResponse(responseCode = "400", description = "Valeur système non"
                    + " supprimable"),
            @APIResponse(responseCode = "404", description = "Référence non trouvée")
    })
    public Response supprimer(
            @PathParam("id") UUID id) {
        try {
            service.supprimer(id);
            return Response.noContent().build();
        } catch (IllegalArgumentException e) {
            return Response
                    .status(Response.Status.BAD_REQUEST)
                    .entity(Map.of("error", e.getMessage()))
                    .build();
        }
    }
}
