package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.api.dto.finance.request.CreateAdhesionRequest;
import dev.lions.unionflow.server.api.dto.finance.request.UpdateAdhesionRequest;
import dev.lions.unionflow.server.api.dto.finance.response.AdhesionResponse;
import dev.lions.unionflow.server.service.AdhesionService;
import jakarta.inject.Inject;
import jakarta.annotation.security.RolesAllowed;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * Resource REST pour la gestion des adhésions
 * Expose les endpoints API pour les opérations CRUD sur les adhésions
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-17
 */
@Path("/api/adhesions")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Adhésions", description = "Gestion des demandes d'adhésion des membres")
@Slf4j
@RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "MEMBRE", "USER" })
public class AdhesionResource {

  @Inject
  AdhesionService adhesionService;

  /** Récupère toutes les adhésions avec pagination */
  @GET
  @Operation(summary = "Lister toutes les adhésions", description = "Récupère la liste paginée de toutes les adhésions")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Liste des adhésions récupérée avec succès", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = AdhesionResponse.class))),
      @APIResponse(responseCode = "400", description = "Paramètres de pagination invalides"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response getAllAdhesions(
      @Parameter(description = "Numéro de page (0-based)", example = "0") @QueryParam("page") @DefaultValue("0") @Min(0) int page,
      @Parameter(description = "Taille de la page", example = "20") @QueryParam("size") @DefaultValue("20") @Min(1) int size) {

    log.info("GET /api/adhesions - page: {}, size: {}", page, size);
    List<AdhesionResponse> adhesions = adhesionService.getAllAdhesions(page, size);
    log.info("Récupération réussie de {} adhésions", adhesions.size());
    return Response.ok(adhesions).build();
  }

  /** Récupère une adhésion par son ID */
  @GET
  @Path("/{id}")
  @Operation(summary = "Récupérer une adhésion par ID", description = "Récupère les détails d'une adhésion spécifique")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Adhésion trouvée", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = AdhesionResponse.class))),
      @APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response getAdhesionById(
      @Parameter(description = "Identifiant de l'adhésion", required = true) @PathParam("id") @NotNull UUID id) {

    log.info("GET /api/adhesions/{}", id);
    AdhesionResponse adhesion = adhesionService.getAdhesionById(id);
    log.info("Adhésion récupérée avec succès - ID: {}", id);
    return Response.ok(adhesion).build();
  }

  /** Récupère une adhésion par son numéro de référence */
  @GET
  @Path("/reference/{numeroReference}")
  @Operation(summary = "Récupérer une adhésion par référence", description = "Récupère une adhésion par son numéro de référence unique")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Adhésion trouvée"),
      @APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response getAdhesionByReference(
      @Parameter(description = "Numéro de référence de l'adhésion", required = true) @PathParam("numeroReference") @NotNull String numeroReference) {

    log.info("GET /api/adhesions/reference/{}", numeroReference);
    AdhesionResponse adhesion = adhesionService.getAdhesionByReference(numeroReference);
    log.info("Adhésion récupérée avec succès - Référence: {}", numeroReference);
    return Response.ok(adhesion).build();
  }

  @POST
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "MEMBRE" })
  @Operation(summary = "Créer une nouvelle adhésion", description = "Crée une nouvelle demande d'adhésion pour un membre")
  @APIResponses({
      @APIResponse(responseCode = "201", description = "Adhésion créée avec succès", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = AdhesionResponse.class))),
      @APIResponse(responseCode = "400", description = "Données invalides"),
      @APIResponse(responseCode = "404", description = "Membre ou organisation non trouvé"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response createAdhesion(
      @Parameter(description = "Données de l'adhésion à créer", required = true) @Valid CreateAdhesionRequest request) {

    log.info(
        "POST /api/adhesions - Création adhésion pour membre: {} et organisation: {}",
        request.membreId(),
        request.organisationId());

    AdhesionResponse nouvelleAdhesion = adhesionService.createAdhesion(request);

    log.info(
        "Adhésion créée avec succès - ID: {}, Référence: {}",
        nouvelleAdhesion.getId(),
        nouvelleAdhesion.getNumeroReference());

    return Response.status(Response.Status.CREATED).entity(nouvelleAdhesion).build();
  }

  /** Met à jour une adhésion existante */
  @PUT
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "MEMBRE" })
  @Path("/{id}")
  @Operation(summary = "Mettre à jour une adhésion", description = "Met à jour les données d'une adhésion existante")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Adhésion mise à jour avec succès"),
      @APIResponse(responseCode = "400", description = "Données invalides"),
      @APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response updateAdhesion(
      @Parameter(description = "Identifiant de l'adhésion", required = true) @PathParam("id") @NotNull UUID id,
      @Parameter(description = "Nouvelles données de l'adhésion", required = true) @Valid UpdateAdhesionRequest request) {

    log.info("PUT /api/adhesions/{}", id);
    AdhesionResponse adhesionMiseAJour = adhesionService.updateAdhesion(id, request);
    log.info("Adhésion mise à jour avec succès - ID: {}", id);
    return Response.ok(adhesionMiseAJour).build();
  }

  /** Supprime une adhésion */
  @DELETE
  @RolesAllowed({ "ADMIN" })
  @Path("/{id}")
  @Operation(summary = "Supprimer une adhésion", description = "Supprime (annule) une adhésion")
  @APIResponses({
      @APIResponse(responseCode = "204", description = "Adhésion supprimée avec succès"),
      @APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
      @APIResponse(responseCode = "409", description = "Impossible de supprimer une adhésion payée"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response deleteAdhesion(
      @Parameter(description = "Identifiant de l'adhésion", required = true) @PathParam("id") @NotNull UUID id) {

    log.info("DELETE /api/adhesions/{}", id);
    adhesionService.deleteAdhesion(id);
    log.info("Adhésion supprimée avec succès - ID: {}", id);
    return Response.noContent().build();
  }

  /** Approuve une adhésion */
  @POST
  @RolesAllowed({ "SUPER_ADMIN", "ADMIN", "ADMIN_ORGANISATION" })
  @Path("/{id}/approuver")
  @Operation(summary = "Approuver une adhésion", description = "Approuve une demande d'adhésion en attente")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Adhésion approuvée avec succès"),
      @APIResponse(responseCode = "400", description = "L'adhésion ne peut pas être approuvée"),
      @APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response approuverAdhesion(
      @Parameter(description = "Identifiant de l'adhésion", required = true) @PathParam("id") @NotNull UUID id,
      @Parameter(description = "Nom de l'utilisateur qui approuve") @QueryParam("approuvePar") String approuvePar) {

    log.info("POST /api/adhesions/{}/approuver", id);
    AdhesionResponse adhesion = adhesionService.approuverAdhesion(id, approuvePar);
    log.info("Adhésion approuvée avec succès - ID: {}", id);
    return Response.ok(adhesion).build();
  }

  /** Rejette une adhésion */
  @POST
  @RolesAllowed({ "SUPER_ADMIN", "ADMIN", "ADMIN_ORGANISATION" })
  @Path("/{id}/rejeter")
  @Operation(summary = "Rejeter une adhésion", description = "Rejette une demande d'adhésion en attente")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Adhésion rejetée avec succès"),
      @APIResponse(responseCode = "400", description = "L'adhésion ne peut pas être rejetée"),
      @APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response rejeterAdhesion(
      @Parameter(description = "Identifiant de l'adhésion", required = true) @PathParam("id") @NotNull UUID id,
      @Parameter(description = "Motif du rejet", required = true) @QueryParam("motifRejet") @NotNull String motifRejet) {

    log.info("POST /api/adhesions/{}/rejeter", id);
    AdhesionResponse adhesion = adhesionService.rejeterAdhesion(id, motifRejet);
    log.info("Adhésion rejetée avec succès - ID: {}", id);
    return Response.ok(adhesion).build();
  }

  /** Enregistre un paiement pour une adhésion */
  @POST
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "MEMBRE" })
  @Path("/{id}/paiement")
  @Operation(summary = "Enregistrer un paiement", description = "Enregistre un paiement pour une adhésion approuvée")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Paiement enregistré avec succès"),
      @APIResponse(responseCode = "400", description = "L'adhésion ne peut pas recevoir de paiement"),
      @APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response enregistrerPaiement(
      @Parameter(description = "Identifiant de l'adhésion", required = true) @PathParam("id") @NotNull UUID id,
      @Parameter(description = "Montant payé", required = true) @QueryParam("montantPaye") @NotNull BigDecimal montantPaye,
      @Parameter(description = "Méthode de paiement") @QueryParam("methodePaiement") String methodePaiement,
      @Parameter(description = "Référence du paiement") @QueryParam("referencePaiement") String referencePaiement) {

    log.info("POST /api/adhesions/{}/paiement", id);
    AdhesionResponse adhesion = adhesionService.enregistrerPaiement(id, montantPaye, methodePaiement,
        referencePaiement);
    log.info("Paiement enregistré avec succès pour l'adhésion - ID: {}", id);
    return Response.ok(adhesion).build();
  }

  /** Récupère les adhésions d'un membre */
  @GET
  @Path("/membre/{membreId}")
  @Operation(summary = "Lister les adhésions d'un membre", description = "Récupère toutes les adhésions d'un membre spécifique")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Liste des adhésions du membre"),
      @APIResponse(responseCode = "404", description = "Membre non trouvé"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response getAdhesionsByMembre(
      @Parameter(description = "Identifiant du membre", required = true) @PathParam("membreId") @NotNull UUID membreId,
      @Parameter(description = "Numéro de page", example = "0") @QueryParam("page") @DefaultValue("0") @Min(0) int page,
      @Parameter(description = "Taille de la page", example = "20") @QueryParam("size") @DefaultValue("20") @Min(1) int size) {

    log.info("GET /api/adhesions/membre/{} - page: {}, size: {}", membreId, page, size);
    List<AdhesionResponse> adhesions = adhesionService.getAdhesionsByMembre(membreId, page, size);
    log.info("Récupération réussie de {} adhésions pour le membre {}", adhesions.size(), membreId);
    return Response.ok(adhesions).build();
  }

  /** Récupère les adhésions d'une organisation */
  @GET
  @Path("/organisation/{organisationId}")
  @Operation(summary = "Lister les adhésions d'une organisation", description = "Récupère toutes les adhésions d'une organisation spécifique")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Liste des adhésions de l'organisation"),
      @APIResponse(responseCode = "404", description = "Organisation non trouvée"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response getAdhesionsByOrganisation(
      @Parameter(description = "Identifiant de l'organisation", required = true) @PathParam("organisationId") @NotNull UUID organisationId,
      @Parameter(description = "Numéro de page", example = "0") @QueryParam("page") @DefaultValue("0") @Min(0) int page,
      @Parameter(description = "Taille de la page", example = "20") @QueryParam("size") @DefaultValue("20") @Min(1) int size) {

    log.info("GET /api/adhesions/organisation/{} - page: {}, size: {}", organisationId, page, size);
    List<AdhesionResponse> adhesions = adhesionService.getAdhesionsByOrganisation(organisationId, page, size);
    log.info("Récupération réussie de {} adhésions pour l'organisation {}", adhesions.size(), organisationId);
    return Response.ok(adhesions).build();
  }

  /** Récupère les adhésions par statut */
  @GET
  @Path("/statut/{statut}")
  @Operation(summary = "Lister les adhésions par statut", description = "Récupère toutes les adhésions ayant un statut spécifique")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Liste des adhésions avec le statut spécifié"),
      @APIResponse(responseCode = "400", description = "Statut invalide"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response getAdhesionsByStatut(
      @Parameter(description = "Statut des adhésions", required = true, example = "EN_ATTENTE") @PathParam("statut") @NotNull String statut,
      @Parameter(description = "Numéro de page", example = "0") @QueryParam("page") @DefaultValue("0") @Min(0) int page,
      @Parameter(description = "Taille de la page", example = "20") @QueryParam("size") @DefaultValue("20") @Min(1) int size) {

    log.info("GET /api/adhesions/statut/{} - page: {}, size: {}", statut, page, size);
    List<AdhesionResponse> adhesions = adhesionService.getAdhesionsByStatut(statut, page, size);
    log.info("Récupération réussie de {} adhésions avec statut {}", adhesions.size(), statut);
    return Response.ok(adhesions).build();
  }

  /** Récupère les adhésions en attente */
  @GET
  @Path("/en-attente")
  @Operation(summary = "Lister les adhésions en attente", description = "Récupère toutes les adhésions en attente d'approbation")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Liste des adhésions en attente"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response getAdhesionsEnAttente(
      @Parameter(description = "Numéro de page", example = "0") @QueryParam("page") @DefaultValue("0") @Min(0) int page,
      @Parameter(description = "Taille de la page", example = "20") @QueryParam("size") @DefaultValue("20") @Min(1) int size) {

    log.info("GET /api/adhesions/en-attente - page: {}, size: {}", page, size);
    List<AdhesionResponse> adhesions = adhesionService.getAdhesionsEnAttente(page, size);
    log.info("Récupération réussie de {} adhésions en attente", adhesions.size());
    return Response.ok(adhesions).build();
  }

  /** Récupère les statistiques des adhésions */
  @GET
  @Path("/stats")
  @Operation(summary = "Statistiques des adhésions", description = "Récupère les statistiques globales des adhésions")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Statistiques récupérées avec succès"),
      @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  public Response getStatistiquesAdhesions() {
    log.info("GET /api/adhesions/stats");
    Map<String, Object> statistiques = adhesionService.getStatistiquesAdhesions();
    log.info("Statistiques récupérées avec succès");
    return Response.ok(statistiques).build();
  }
}
