package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.api.dto.common.PagedResponse;
import dev.lions.unionflow.server.dto.EvenementMobileDTO;
import dev.lions.unionflow.server.entity.Evenement;
import dev.lions.unionflow.server.entity.FeedbackEvenement;
import dev.lions.unionflow.server.entity.InscriptionEvenement;
import dev.lions.unionflow.server.service.EvenementService;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.HashMap;
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.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;

/**
 * Resource REST pour la gestion des événements
 *
 * <p>
 * Fournit les endpoints API pour les opérations CRUD sur les événements,
 * optimisé pour
 * l'intégration avec l'application mobile UnionFlow.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-15
 */
@Path("/api/evenements")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Événements", description = "Gestion des événements de l'union")
public class EvenementResource {

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

  @Inject
  EvenementService evenementService;

  /** Endpoint de test public pour vérifier la connectivité */
  @GET
  @Path("/test")
  @PermitAll
  @Operation(summary = "Test de connectivité", description = "Endpoint public pour tester la connectivité")
  @APIResponse(responseCode = "200", description = "Test réussi")
  public Response testConnectivity() {
    LOG.info("Test de connectivité appelé depuis l'application mobile");
    return Response.ok(
        Map.of(
            "status", "success",
            "message", "Serveur UnionFlow opérationnel",
            "timestamp", System.currentTimeMillis(),
            "version", "1.0.0"))
        .build();
  }

  /** Endpoint de debug pour vérifier le chargement des données */
  @GET
  @Path("/count")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "SUPER_ADMIN" })
  @Operation(summary = "Compter les événements", description = "Compte le nombre d'événements dans la base")
  @APIResponse(responseCode = "200", description = "Nombre d'événements")
  public Response countEvenements() {
    long count = evenementService.countEvenements();
    return Response.ok(Map.of("count", count, "status", "success")).build();
  }

  /** Liste tous les événements actifs avec pagination */
  @GET
  @Operation(summary = "Lister tous les événements actifs", description = "Récupère la liste paginée des événements actifs")
  @APIResponse(responseCode = "200", description = "Liste des événements actifs")
  @APIResponse(responseCode = "401", description = "Non authentifié")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
  public PagedResponse<EvenementMobileDTO> listerEvenements(
      @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,
      @Parameter(description = "Champ de tri", example = "dateDebut") @QueryParam("sort") @DefaultValue("dateDebut") String sortField,
      @Parameter(description = "Direction du tri (asc/desc)", example = "asc") @QueryParam("direction") @DefaultValue("asc") String sortDirection) {

    LOG.infof("GET /api/evenements - page: %d, size: %d", page, size);

    Sort sort = sortDirection.equalsIgnoreCase("desc")
        ? Sort.by(sortField).descending()
        : Sort.by(sortField).ascending();

    List<Evenement> evenements = evenementService.listerEvenementsActifs(Page.of(page, size), sort);

    LOG.infof("Nombre d'événements récupérés: %d", evenements.size());

    // Convertir en DTO mobile
    List<EvenementMobileDTO> evenementsDTOs = new ArrayList<>();
    for (Evenement evenement : evenements) {
      EvenementMobileDTO dto = EvenementMobileDTO.fromEntity(evenement);
      evenementsDTOs.add(dto);
    }

    LOG.infof("Nombre de DTOs créés: %d", evenementsDTOs.size());

    // Compter le total d'événements actifs
    long total = evenementService.countEvenementsActifs();

    LOG.infof("Réponse prête: %d événements, total=%d", evenementsDTOs.size(), total);

    return new PagedResponse<>(evenementsDTOs, total, page, size);
  }

  /** Récupère un événement par son ID */
  @GET
  @Path("/{id}")
  @Operation(summary = "Récupérer un événement par ID")
  @APIResponse(responseCode = "200", description = "Événement trouvé")
  @APIResponse(responseCode = "404", description = "Événement non trouvé")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE" })
  public Response obtenirEvenement(
      @Parameter(description = "UUID de l'événement", required = true) @PathParam("id") UUID id) {

    LOG.infof("GET /api/evenements/%s", id);

    Evenement evenement = evenementService.trouverParId(id)
        .orElseThrow(() -> new NotFoundException("Événement non trouvé avec l'ID: " + id));

    return Response.ok(evenement).build();
  }

  /** Crée un nouvel événement */
  @POST
  @Operation(summary = "Créer un nouvel événement")
  @APIResponse(responseCode = "201", description = "Événement créé avec succès")
  @APIResponse(responseCode = "400", description = "Données invalides")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT" })
  public Response creerEvenement(
      @Parameter(description = "Données de l'événement à créer", required = true) @Valid Evenement evenement) {

    LOG.infof("POST /api/evenements - Création événement: %s", evenement.getTitre());
    Evenement evenementCree = evenementService.creerEvenement(evenement);
    return Response.status(Response.Status.CREATED).entity(evenementCree).build();
  }

  /** Met à jour un événement existant */
  @PUT
  @Path("/{id}")
  @Operation(summary = "Mettre à jour un événement")
  @APIResponse(responseCode = "200", description = "Événement mis à jour avec succès")
  @APIResponse(responseCode = "404", description = "Événement non trouvé")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT" })
  public Response mettreAJourEvenement(@PathParam("id") UUID id, @Valid Evenement evenement) {

    LOG.infof("PUT /api/evenements/%s", id);
    Evenement evenementMisAJour = evenementService.mettreAJourEvenement(id, evenement);
    return Response.ok(evenementMisAJour).build();
  }

  /** Supprime un événement */
  @DELETE
  @Path("/{id}")
  @Operation(summary = "Supprimer un événement")
  @APIResponse(responseCode = "204", description = "Événement supprimé avec succès")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "ORGANISATEUR_EVENEMENT" })
  public Response supprimerEvenement(@PathParam("id") UUID id) {

    LOG.infof("DELETE /api/evenements/%s", id);
    evenementService.supprimerEvenement(id);
    return Response.noContent().build();
  }

  /** Endpoints spécialisés pour l'application mobile */

  /** Liste les événements à venir */
  @GET
  @Path("/a-venir")
  @Operation(summary = "Événements à venir")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE" })
  public Response evenementsAVenir(
      @QueryParam("page") @DefaultValue("0") int page,
      @QueryParam("size") @DefaultValue("10") int size) {

    List<Evenement> evenements = evenementService.listerEvenementsAVenir(
        Page.of(page, size), Sort.by("dateDebut").ascending());

    return Response.ok(evenements).build();
  }

  /** Liste les événements publics */
  @GET
  @Path("/publics")
  @PermitAll
  @Operation(summary = "Événements publics")
  public Response evenementsPublics(
      @QueryParam("page") @DefaultValue("0") int page,
      @QueryParam("size") @DefaultValue("20") int size) {

    List<Evenement> evenements = evenementService.listerEvenementsPublics(
        Page.of(page, size), Sort.by("dateDebut").ascending());

    return Response.ok(evenements).build();
  }

  /** Recherche d'événements */
  @GET
  @Path("/recherche")
  @Operation(summary = "Rechercher des événements")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE" })
  public Response rechercherEvenements(
      @QueryParam("q") String recherche,
      @QueryParam("page") @DefaultValue("0") int page,
      @QueryParam("size") @DefaultValue("20") int size) {

    if (recherche == null || recherche.trim().isEmpty()) {
      return Response.status(Response.Status.BAD_REQUEST)
          .entity(Map.of("error", "Le terme de recherche est obligatoire"))
          .build();
    }

    List<Evenement> evenements = evenementService.rechercherEvenements(
        recherche, Page.of(page, size), Sort.by("dateDebut").ascending());

    return Response.ok(evenements).build();
  }

  /** Événements par type */
  @GET
  @Path("/type/{type}")
  @Operation(summary = "Événements par type")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE" })
  public Response evenementsParType(
      @PathParam("type") String type,
      @QueryParam("page") @DefaultValue("0") int page,
      @QueryParam("size") @DefaultValue("20") int size) {

    List<Evenement> evenements = evenementService.listerParType(
        type, Page.of(page, size), Sort.by("dateDebut").ascending());

    return Response.ok(evenements).build();
  }

  /** Change le statut d'un événement */
  @PATCH
  @Path("/{id}/statut")
  @Operation(summary = "Changer le statut d'un événement")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "ORGANISATEUR_EVENEMENT" })
  public Response changerStatut(
      @PathParam("id") UUID id, @QueryParam("statut") String nouveauStatut) {

    if (nouveauStatut == null) {
      return Response.status(Response.Status.BAD_REQUEST)
          .entity(Map.of("error", "Le nouveau statut est obligatoire"))
          .build();
    }

    Evenement evenement = evenementService.changerStatut(id, nouveauStatut);

    return Response.ok(evenement).build();
  }

  /** Statistiques des événements */
  @GET
  @Path("/statistiques")
  @Operation(summary = "Statistiques des événements")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT" })
  public Response obtenirStatistiques() {

    Map<String, Object> statistiques = evenementService.obtenirStatistiques();
    return Response.ok(statistiques).build();
  }

  /** Indique si l'utilisateur connecté est inscrit à l'événement (pour l'app mobile). */
  @GET
  @Path("/{id}/me/inscrit")
  @Operation(summary = "Statut d'inscription de l'utilisateur connecté")
  @APIResponse(responseCode = "200", description = "Statut d'inscription")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
  public Response meInscrit(
      @Parameter(description = "UUID de l'événement", required = true) @PathParam("id") UUID id) {
    boolean inscrit = evenementService.isUserInscrit(id);
    return Response.ok(Map.of("inscrit", inscrit)).build();
  }

  // === GESTION DES INSCRIPTIONS ===

  /** S'inscrire à un événement */
  @POST
  @Path("/{id}/inscriptions")
  @Operation(summary = "S'inscrire à un événement")
  @APIResponse(responseCode = "201", description = "Inscription créée")
  @APIResponse(responseCode = "400", description = "Déjà inscrit ou événement complet")
  @APIResponse(responseCode = "404", description = "Événement non trouvé")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
  @Transactional
  public Response inscrireEvenement(@PathParam("id") UUID evenementId) {
    try {
      InscriptionEvenement inscription = evenementService.inscrireEvenement(evenementId);
      return Response.status(Response.Status.CREATED).entity(inscription).build();
    } catch (IllegalStateException e) {
      return Response.status(Response.Status.BAD_REQUEST)
          .entity(Map.of("error", e.getMessage()))
          .build();
    }
  }

  /** Se désinscrire d'un événement */
  @DELETE
  @Path("/{id}/inscriptions")
  @Operation(summary = "Se désinscrire d'un événement")
  @APIResponse(responseCode = "204", description = "Désinscription effectuée")
  @APIResponse(responseCode = "404", description = "Inscription non trouvée")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
  @Transactional
  public Response desinscrireEvenement(@PathParam("id") UUID evenementId) {
    evenementService.desinscrireEvenement(evenementId);
    return Response.noContent().build();
  }

  /** Liste des participants d'un événement */
  @GET
  @Path("/{id}/participants")
  @Operation(summary = "Liste des participants")
  @APIResponse(responseCode = "200", description = "Liste des participants")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE" })
  public Response getParticipants(@PathParam("id") UUID evenementId) {
    List<InscriptionEvenement> participants = evenementService.getParticipants(evenementId);
    return Response.ok(participants).build();
  }

  /** Mes inscriptions */
  @GET
  @Path("/mes-inscriptions")
  @Operation(summary = "Mes inscriptions aux événements")
  @APIResponse(responseCode = "200", description = "Liste de mes inscriptions")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
  public Response getMesInscriptions() {
    List<InscriptionEvenement> inscriptions = evenementService.getMesInscriptions();
    return Response.ok(inscriptions).build();
  }

  // === GESTION DES FEEDBACKS ===

  /** Soumettre un feedback */
  @POST
  @Path("/{id}/feedback")
  @Operation(summary = "Soumettre un feedback sur l'événement")
  @APIResponse(responseCode = "201", description = "Feedback créé")
  @APIResponse(responseCode = "400", description = "Données invalides ou feedback déjà soumis")
  @APIResponse(responseCode = "404", description = "Événement non trouvé")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
  @Transactional
  public Response soumetteFeedback(
      @PathParam("id") UUID evenementId, Map<String, Object> requestBody) {

    Integer note = (Integer) requestBody.get("note");
    String commentaire = (String) requestBody.get("commentaire");

    if (note == null || note < 1 || note > 5) {
      return Response.status(Response.Status.BAD_REQUEST)
          .entity(Map.of("error", "La note doit être entre 1 et 5"))
          .build();
    }

    try {
      FeedbackEvenement feedback =
          evenementService.soumetteFeedback(evenementId, note, commentaire);
      return Response.status(Response.Status.CREATED).entity(feedback).build();
    } catch (IllegalStateException e) {
      return Response.status(Response.Status.BAD_REQUEST)
          .entity(Map.of("error", e.getMessage()))
          .build();
    }
  }

  /** Liste des feedbacks d'un événement */
  @GET
  @Path("/{id}/feedbacks")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
  @Operation(summary = "Liste des feedbacks de l'événement")
  @APIResponse(responseCode = "200", description = "Liste des feedbacks")
  public Response getFeedbacks(@PathParam("id") UUID evenementId) {
    List<FeedbackEvenement> feedbacks = evenementService.getFeedbacks(evenementId);
    Map<String, Object> stats = evenementService.getStatistiquesFeedback(evenementId);

    return Response.ok(
            Map.of(
                "feedbacks", feedbacks,
                "noteMoyenne", stats.get("noteMoyenne"),
                "nombreFeedbacks", stats.get("nombreFeedbacks")))
        .build();
  }
}
