package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.api.dto.membre.MembreDTO;
import dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria;
import dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.service.MembreService;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
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.ExampleObject;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;

/** Resource REST pour la gestion des membres */
@Path("/api/membres")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@ApplicationScoped
@Tag(name = "Membres", description = "API de gestion des membres")
public class MembreResource {

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

  @Inject MembreService membreService;

  @GET
  @Operation(summary = "Lister tous les membres actifs")
  @APIResponse(responseCode = "200", description = "Liste des membres actifs")
  public Response listerMembres(
      @Parameter(description = "Numéro de page (0-based)") @QueryParam("page") @DefaultValue("0")
          int page,
      @Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("20")
          int size,
      @Parameter(description = "Champ de tri") @QueryParam("sort") @DefaultValue("nom")
          String sortField,
      @Parameter(description = "Direction du tri (asc/desc)")
          @QueryParam("direction")
          @DefaultValue("asc")
          String sortDirection) {

    LOG.infof("Récupération de la liste des membres actifs - page: %d, size: %d", page, size);

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

    List<Membre> membres = membreService.listerMembresActifs(Page.of(page, size), sort);
    List<MembreDTO> membresDTO = membreService.convertToDTOList(membres);

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

  @GET
  @Path("/{id}")
  @Operation(summary = "Récupérer un membre par son ID")
  @APIResponse(responseCode = "200", description = "Membre trouvé")
  @APIResponse(responseCode = "404", description = "Membre non trouvé")
  public Response obtenirMembre(@Parameter(description = "UUID du membre") @PathParam("id") UUID id) {
    LOG.infof("Récupération du membre ID: %s", id);
    return membreService
        .trouverParId(id)
        .map(
            membre -> {
              MembreDTO membreDTO = membreService.convertToDTO(membre);
              return Response.ok(membreDTO).build();
            })
        .orElse(
            Response.status(Response.Status.NOT_FOUND)
                .entity(Map.of("message", "Membre non trouvé"))
                .build());
  }

  @POST
  @PermitAll
  @Operation(summary = "Créer un nouveau membre")
  @APIResponse(responseCode = "201", description = "Membre créé avec succès")
  @APIResponse(responseCode = "400", description = "Données invalides")
  public Response creerMembre(@Valid MembreDTO membreDTO) {
    LOG.infof("Création d'un nouveau membre: %s", membreDTO.getEmail());
    try {
      // Conversion DTO vers entité
      Membre membre = membreService.convertFromDTO(membreDTO);

      // Création du membre
      Membre nouveauMembre = membreService.creerMembre(membre);

      // Conversion de retour vers DTO
      MembreDTO nouveauMembreDTO = membreService.convertToDTO(nouveauMembre);

      return Response.status(Response.Status.CREATED).entity(nouveauMembreDTO).build();
    } catch (IllegalArgumentException e) {
      return Response.status(Response.Status.BAD_REQUEST)
          .entity(Map.of("message", e.getMessage()))
          .build();
    }
  }

  @PUT
  @Path("/{id}")
  @Operation(summary = "Mettre à jour un membre existant")
  @APIResponse(responseCode = "200", description = "Membre mis à jour avec succès")
  @APIResponse(responseCode = "404", description = "Membre non trouvé")
  @APIResponse(responseCode = "400", description = "Données invalides")
  public Response mettreAJourMembre(
      @Parameter(description = "UUID du membre") @PathParam("id") UUID id,
      @Valid MembreDTO membreDTO) {
    LOG.infof("Mise à jour du membre ID: %s", id);
    try {
      // Conversion DTO vers entité
      Membre membre = membreService.convertFromDTO(membreDTO);

      // Mise à jour du membre
      Membre membreMisAJour = membreService.mettreAJourMembre(id, membre);

      // Conversion de retour vers DTO
      MembreDTO membreMisAJourDTO = membreService.convertToDTO(membreMisAJour);

      return Response.ok(membreMisAJourDTO).build();
    } catch (IllegalArgumentException e) {
      return Response.status(Response.Status.BAD_REQUEST)
          .entity(Map.of("message", e.getMessage()))
          .build();
    }
  }

  @DELETE
  @Path("/{id}")
  @Operation(summary = "Désactiver un membre")
  @APIResponse(responseCode = "204", description = "Membre désactivé avec succès")
  @APIResponse(responseCode = "404", description = "Membre non trouvé")
  public Response desactiverMembre(
      @Parameter(description = "UUID du membre") @PathParam("id") UUID id) {
    LOG.infof("Désactivation du membre ID: %s", id);
    try {
      membreService.desactiverMembre(id);
      return Response.noContent().build();
    } catch (IllegalArgumentException e) {
      return Response.status(Response.Status.NOT_FOUND)
          .entity(Map.of("message", e.getMessage()))
          .build();
    }
  }

  @GET
  @Path("/recherche")
  @Operation(summary = "Rechercher des membres par nom ou prénom")
  @APIResponse(responseCode = "200", description = "Résultats de la recherche")
  public Response rechercherMembres(
      @Parameter(description = "Terme de recherche") @QueryParam("q") String recherche,
      @Parameter(description = "Numéro de page (0-based)") @QueryParam("page") @DefaultValue("0")
          int page,
      @Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("20")
          int size,
      @Parameter(description = "Champ de tri") @QueryParam("sort") @DefaultValue("nom")
          String sortField,
      @Parameter(description = "Direction du tri (asc/desc)")
          @QueryParam("direction")
          @DefaultValue("asc")
          String sortDirection) {

    LOG.infof("Recherche de membres avec le terme: %s", recherche);
    if (recherche == null || recherche.trim().isEmpty()) {
      return Response.status(Response.Status.BAD_REQUEST)
          .entity(Map.of("message", "Le terme de recherche est requis"))
          .build();
    }

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

    List<Membre> membres =
        membreService.rechercherMembres(recherche.trim(), Page.of(page, size), sort);
    List<MembreDTO> membresDTO = membreService.convertToDTOList(membres);

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

  @GET
  @Path("/stats")
  @Operation(summary = "Obtenir les statistiques avancées des membres")
  @APIResponse(responseCode = "200", description = "Statistiques complètes des membres")
  public Response obtenirStatistiques() {
    LOG.info("Récupération des statistiques avancées des membres");
    Map<String, Object> statistiques = membreService.obtenirStatistiquesAvancees();
    return Response.ok(statistiques).build();
  }

  @GET
  @Path("/autocomplete/villes")
  @Operation(summary = "Obtenir la liste des villes pour autocomplétion")
  @APIResponse(responseCode = "200", description = "Liste des villes distinctes")
  public Response obtenirVilles(
      @Parameter(description = "Terme de recherche (optionnel)") @QueryParam("query") String query) {
    LOG.infof("Récupération des villes pour autocomplétion - query: %s", query);
    List<String> villes = membreService.obtenirVillesDistinctes(query);
    return Response.ok(villes).build();
  }

  @GET
  @Path("/autocomplete/professions")
  @Operation(summary = "Obtenir la liste des professions pour autocomplétion")
  @APIResponse(responseCode = "200", description = "Liste des professions distinctes")
  public Response obtenirProfessions(
      @Parameter(description = "Terme de recherche (optionnel)") @QueryParam("query") String query) {
    LOG.infof("Récupération des professions pour autocomplétion - query: %s", query);
    List<String> professions = membreService.obtenirProfessionsDistinctes(query);
    return Response.ok(professions).build();
  }

  @GET
  @Path("/recherche-avancee")
  @Operation(summary = "Recherche avancée de membres avec filtres multiples (DEPRECATED)")
  @APIResponse(responseCode = "200", description = "Résultats de la recherche avancée")
  @Deprecated
  public Response rechercheAvancee(
      @Parameter(description = "Terme de recherche") @QueryParam("q") String recherche,
      @Parameter(description = "Statut actif (true/false)") @QueryParam("actif") Boolean actif,
      @Parameter(description = "Date d'adhésion minimum (YYYY-MM-DD)")
          @QueryParam("dateAdhesionMin")
          String dateAdhesionMin,
      @Parameter(description = "Date d'adhésion maximum (YYYY-MM-DD)")
          @QueryParam("dateAdhesionMax")
          String dateAdhesionMax,
      @Parameter(description = "Numéro de page (0-based)") @QueryParam("page") @DefaultValue("0")
          int page,
      @Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("20")
          int size,
      @Parameter(description = "Champ de tri") @QueryParam("sort") @DefaultValue("nom")
          String sortField,
      @Parameter(description = "Direction du tri (asc/desc)")
          @QueryParam("direction")
          @DefaultValue("asc")
          String sortDirection) {

    LOG.infof(
        "Recherche avancée de membres (DEPRECATED) - recherche: %s, actif: %s", recherche, actif);

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

      // Conversion des dates si fournies
      java.time.LocalDate dateMin =
          dateAdhesionMin != null ? java.time.LocalDate.parse(dateAdhesionMin) : null;
      java.time.LocalDate dateMax =
          dateAdhesionMax != null ? java.time.LocalDate.parse(dateAdhesionMax) : null;

      List<Membre> membres =
          membreService.rechercheAvancee(
              recherche, actif, dateMin, dateMax, Page.of(page, size), sort);
      List<MembreDTO> membresDTO = membreService.convertToDTOList(membres);

      return Response.ok(membresDTO).build();
    } catch (Exception e) {
      LOG.errorf("Erreur lors de la recherche avancée: %s", e.getMessage());
      return Response.status(Response.Status.BAD_REQUEST)
          .entity(Map.of("message", "Erreur dans les paramètres de recherche: " + e.getMessage()))
          .build();
    }
  }

  /**
   * Nouvelle recherche avancée avec critères complets et résultats enrichis Réservée aux super
   * administrateurs pour des recherches sophistiquées
   */
  @POST
  @Path("/search/advanced")
  @RolesAllowed({"SUPER_ADMIN", "ADMIN"})
  @Operation(
      summary = "Recherche avancée de membres avec critères multiples",
      description =
          """
          Recherche sophistiquée de membres avec de nombreux critères de filtrage :
          - Recherche textuelle dans nom, prénom, email
          - Filtres par organisation, rôles, statut
          - Filtres par âge, région, profession
          - Filtres par dates d'adhésion
          - Résultats paginés avec statistiques

          Réservée aux super administrateurs et administrateurs.
          """)
  @APIResponses({
    @APIResponse(
        responseCode = "200",
        description = "Recherche effectuée avec succès",
        content =
            @Content(
                mediaType = MediaType.APPLICATION_JSON,
                schema = @Schema(implementation = MembreSearchResultDTO.class),
                examples =
                    @ExampleObject(
                        name = "Exemple de résultats",
                        value =
                            """
                            {
                              "membres": [...],
                              "totalElements": 247,
                              "totalPages": 13,
                              "currentPage": 0,
                              "pageSize": 20,
                              "hasNext": true,
                              "hasPrevious": false,
                              "executionTimeMs": 45,
                              "statistics": {
                                "membresActifs": 230,
                                "membresInactifs": 17,
                                "ageMoyen": 34.5,
                                "nombreOrganisations": 12
                              }
                            }
                            """))),
    @APIResponse(
        responseCode = "400",
        description = "Critères de recherche invalides",
        content =
            @Content(
                mediaType = MediaType.APPLICATION_JSON,
                examples =
                    @ExampleObject(
                        value =
                            """
{
  "message": "Critères de recherche invalides",
  "details": "La date minimum ne peut pas être postérieure à la date maximum"
}
"""))),
    @APIResponse(
        responseCode = "403",
        description = "Accès non autorisé - Rôle SUPER_ADMIN ou ADMIN requis"),
    @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
  })
  @SecurityRequirement(name = "keycloak")
  public Response searchMembresAdvanced(
      @RequestBody(
              description = "Critères de recherche avancée",
              required = true,
              content =
                  @Content(
                      mediaType = MediaType.APPLICATION_JSON,
                      schema = @Schema(implementation = MembreSearchCriteria.class),
                      examples =
                          @ExampleObject(
                              name = "Exemple de critères",
                              value =
                                  """
                                  {
                                    "query": "marie",
                                    "statut": "ACTIF",
                                    "ageMin": 25,
                                    "ageMax": 45,
                                    "region": "Dakar",
                                    "roles": ["PRESIDENT", "SECRETAIRE"],
                                    "dateAdhesionMin": "2020-01-01",
                                    "includeInactifs": false
                                  }
                                  """)))
          @Valid
          MembreSearchCriteria criteria,
      @Parameter(description = "Numéro de page (0-based)", example = "0")
          @QueryParam("page")
          @DefaultValue("0")
          int page,
      @Parameter(description = "Taille de la page", example = "20")
          @QueryParam("size")
          @DefaultValue("20")
          int size,
      @Parameter(description = "Champ de tri", example = "nom")
          @QueryParam("sort")
          @DefaultValue("nom")
          String sortField,
      @Parameter(description = "Direction du tri (asc/desc)", example = "asc")
          @QueryParam("direction")
          @DefaultValue("asc")
          String sortDirection) {

    long startTime = System.currentTimeMillis();

    LOG.infof(
        "Recherche avancée de membres - critères: %s, page: %d, size: %d",
        criteria.getDescription(), page, size);

    try {
      // Validation des critères
      if (criteria == null) {
        return Response.status(Response.Status.BAD_REQUEST)
            .entity(Map.of("message", "Les critères de recherche sont requis"))
            .build();
      }

      // Nettoyage et validation des critères
      criteria.sanitize();

      if (!criteria.hasAnyCriteria()) {
        return Response.status(Response.Status.BAD_REQUEST)
            .entity(Map.of("message", "Au moins un critère de recherche doit être spécifié"))
            .build();
      }

      if (!criteria.isValid()) {
        return Response.status(Response.Status.BAD_REQUEST)
            .entity(
                Map.of(
                    "message", "Critères de recherche invalides",
                    "details", "Vérifiez la cohérence des dates et des âges"))
            .build();
      }

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

      // Exécution de la recherche
      MembreSearchResultDTO result =
          membreService.searchMembresAdvanced(criteria, Page.of(page, size), sort);

      // Calcul du temps d'exécution
      long executionTime = System.currentTimeMillis() - startTime;
      result.setExecutionTimeMs(executionTime);

      LOG.infof(
          "Recherche avancée terminée - %d résultats trouvés en %d ms",
          result.getTotalElements(), executionTime);

      return Response.ok(result).build();

    } catch (IllegalArgumentException e) {
      LOG.warnf("Erreur de validation dans la recherche avancée: %s", e.getMessage());
      return Response.status(Response.Status.BAD_REQUEST)
          .entity(Map.of("message", "Paramètres de recherche invalides", "details", e.getMessage()))
          .build();
    } catch (Exception e) {
      LOG.errorf(e, "Erreur lors de la recherche avancée de membres");
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
          .entity(Map.of("message", "Erreur interne lors de la recherche", "error", e.getMessage()))
          .build();
    }
  }

  @POST
  @Path("/export/selection")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
  @Operation(summary = "Exporter une sélection de membres en Excel")
  @APIResponse(responseCode = "200", description = "Fichier Excel généré")
  public Response exporterSelectionMembres(
      @Parameter(description = "Liste des IDs des membres à exporter") List<UUID> membreIds,
      @Parameter(description = "Format d'export") @QueryParam("format") @DefaultValue("EXCEL") String format) {
    LOG.infof("Export de %d membres sélectionnés", membreIds.size());
    try {
      byte[] excelData = membreService.exporterMembresSelectionnes(membreIds, format);
      return Response.ok(excelData)
          .header("Content-Disposition", "attachment; filename=\"membres_selection_" + 
              java.time.LocalDate.now() + "." + (format != null ? format.toLowerCase() : "xlsx") + "\"")
          .build();
    } catch (Exception e) {
      LOG.errorf(e, "Erreur lors de l'export de la sélection");
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
          .entity(Map.of("error", "Erreur lors de l'export: " + e.getMessage()))
          .build();
    }
  }
}
