package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.api.dto.cotisation.request.CreateCotisationRequest;
import dev.lions.unionflow.server.api.dto.cotisation.request.UpdateCotisationRequest;
import dev.lions.unionflow.server.api.dto.cotisation.response.CotisationResponse;
import dev.lions.unionflow.server.api.dto.cotisation.response.CotisationSummaryResponse;
import dev.lions.unionflow.server.service.CotisationService;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
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.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
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.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;

/**
 * Resource REST pour la gestion des cotisations.
 * Expose les endpoints API pour les opérations CRUD sur les cotisations.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-15
 */
@Path("/api/cotisations")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Cotisations", description = "Gestion des cotisations des membres")
@RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "MEMBRE", "USER" })
@Slf4j
public class CotisationResource {

  @Inject
  CotisationService cotisationService;

  /**
   * Endpoint public pour les cotisations (test).
   */
  @GET
  @Path("/public")
  @Operation(summary = "Cotisations publiques", description = "Liste des cotisations simplifiée")
  @APIResponse(responseCode = "200", description = "Liste des cotisations")
  public Response getCotisationsPublic(
      @QueryParam("page") @DefaultValue("0") @Min(0) int page,
      @QueryParam("size") @DefaultValue("20") @Min(1) int size) {

    try {
      log.info("GET /api/cotisations/public - page: {}, size: {}", page, size);

      List<CotisationResponse> cotisations = cotisationService.getAllCotisations(page, size);

      List<Map<String, Object>> content = cotisations.stream()
          .map(c -> {
            Map<String, Object> map = new java.util.HashMap<>();
            map.put("id", c.getId() != null ? c.getId().toString() : null);
            map.put("reference", c.getNumeroReference());
            map.put("nomMembre", c.getNomMembre());
            map.put("montantDu", c.getMontantDu());
            map.put("montantPaye", c.getMontantPaye());
            map.put("statut", c.getStatut());
            map.put("statutLibelle", c.getStatutLibelle());
            map.put("dateEcheance", c.getDateEcheance() != null ? c.getDateEcheance().toString() : null);
            return map;
          })
          .collect(Collectors.toList());

      long totalElements = cotisationService.getStatistiquesCotisations().get("totalCotisations") != null
          ? ((Number) cotisationService.getStatistiquesCotisations().get("totalCotisations")).longValue()
          : content.size();
      int totalPages = (int) Math.ceil((double) totalElements / size);

      Map<String, Object> response = Map.of(
          "content", content,
          "totalElements", totalElements,
          "totalPages", totalPages,
          "size", size,
          "number", page);

      return Response.ok(response).build();
    } catch (Exception e) {
      log.error("Erreur lors de la récupération des cotisations publiques", e);
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
          .entity(Map.of("error", "Erreur lors de la récupération des cotisations"))
          .build();
    }
  }

  /**
   * Récupère toutes les cotisations avec pagination.
   */
  @GET
  @Operation(summary = "Lister toutes les cotisations", description = "Récupère la liste paginée (format résumé)")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Liste récupérée", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CotisationSummaryResponse.class))),
      @APIResponse(responseCode = "500", description = "Erreur interne")
  })
  public Response getAllCotisations(
      @QueryParam("page") @DefaultValue("0") @Min(0) int page,
      @QueryParam("size") @DefaultValue("20") @Min(1) int size) {

    try {
      log.info("GET /api/cotisations - page: {}, size: {}", page, size);
      List<CotisationResponse> cotisations = cotisationService.getAllCotisations(page, size);
      return Response.ok(cotisations).build();
    } catch (Exception e) {
      log.error("Erreur lister cotisations", e);
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
          .entity(Map.of("error", "Erreur récupération cotisations", "message", e.getMessage()))
          .build();
    }
  }

  /**
   * Récupère une cotisation par son ID.
   */
  @GET
  @Path("/{id}")
  @Operation(summary = "Détails d'une cotisation", description = "Récupère les détails complets")
  @APIResponses({
      @APIResponse(responseCode = "200", description = "Succès", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CotisationResponse.class))),
      @APIResponse(responseCode = "404", description = "Non trouvé")
  })
  public Response getCotisationById(@PathParam("id") @NotNull UUID id) {
    try {
      CotisationResponse result = cotisationService.getCotisationById(id);
      return Response.ok(result).build();
    } catch (NotFoundException e) {
      return Response.status(Response.Status.NOT_FOUND).entity(Map.of("error", e.getMessage())).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Récupère une cotisation par sa référence.
   */
  @GET
  @Path("/reference/{numeroReference}")
  @Operation(summary = "Cotisation par référence")
  public Response getCotisationByReference(@PathParam("numeroReference") @NotNull String numeroReference) {
    try {
      CotisationResponse result = cotisationService.getCotisationByReference(numeroReference);
      return Response.ok(result).build();
    } catch (NotFoundException e) {
      return Response.status(Response.Status.NOT_FOUND).entity(Map.of("error", e.getMessage())).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Crée une nouvelle cotisation.
   */
  @POST
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "MEMBRE" })
  @Operation(summary = "Créer une cotisation")
  @APIResponses({
      @APIResponse(responseCode = "201", description = "Créée", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = CotisationResponse.class))),
      @APIResponse(responseCode = "400", description = "Invalide")
  })
  public Response createCotisation(@Valid CreateCotisationRequest request) {
    try {
      CotisationResponse result = cotisationService.createCotisation(request);
      return Response.status(Response.Status.CREATED).entity(result).build();
    } catch (NotFoundException e) {
      return Response.status(Response.Status.NOT_FOUND).entity(Map.of("error", e.getMessage())).build();
    } catch (IllegalArgumentException e) {
      return Response.status(Response.Status.BAD_REQUEST).entity(Map.of("error", e.getMessage())).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Met à jour une cotisation.
   */
  @PUT
  @Path("/{id}")
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "MEMBRE" })
  @Operation(summary = "Mettre à jour une cotisation")
  public Response updateCotisation(@PathParam("id") @NotNull UUID id, @Valid UpdateCotisationRequest request) {
    try {
      CotisationResponse result = cotisationService.updateCotisation(id, request);
      return Response.ok(result).build();
    } catch (NotFoundException e) {
      return Response.status(Response.Status.NOT_FOUND).entity(Map.of("error", e.getMessage())).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Supprime (annule) une cotisation.
   */
  @DELETE
  @Path("/{id}")
  @RolesAllowed({ "ADMIN" })
  @Operation(summary = "Annuler une cotisation")
  public Response deleteCotisation(@PathParam("id") @NotNull UUID id) {
    try {
      cotisationService.deleteCotisation(id);
      return Response.noContent().build();
    } catch (NotFoundException e) {
      return Response.status(Response.Status.NOT_FOUND).entity(Map.of("error", e.getMessage())).build();
    } catch (IllegalStateException e) {
      return Response.status(Response.Status.CONFLICT).entity(Map.of("error", e.getMessage())).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Liste les cotisations d'un membre.
   */
  @GET
  @Path("/membre/{membreId}")
  @Operation(summary = "Cotisations d'un membre")
  public Response getCotisationsByMembre(
      @PathParam("membreId") @NotNull UUID membreId,
      @QueryParam("page") @DefaultValue("0") int page,
      @QueryParam("size") @DefaultValue("20") int size) {
    try {
      List<CotisationResponse> results = cotisationService.getCotisationsByMembre(membreId, page, size);
      return Response.ok(results).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Liste les cotisations par statut.
   */
  @GET
  @Path("/statut/{statut}")
  @Operation(summary = "Cotisations par statut")
  public Response getCotisationsByStatut(
      @PathParam("statut") @NotNull String statut,
      @QueryParam("page") @DefaultValue("0") int page,
      @QueryParam("size") @DefaultValue("20") int size) {
    try {
      List<CotisationResponse> results = cotisationService.getCotisationsByStatut(statut, page, size);
      return Response.ok(results).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Liste les cotisations en retard.
   */
  @GET
  @Path("/en-retard")
  @Operation(summary = "Cotisations en retard")
  public Response getCotisationsEnRetard(
      @QueryParam("page") @DefaultValue("0") int page,
      @QueryParam("size") @DefaultValue("20") int size) {
    try {
      List<CotisationResponse> results = cotisationService.getCotisationsEnRetard(page, size);
      return Response.ok(results).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Recherche avancée.
   */
  @GET
  @Path("/recherche")
  @Operation(summary = "Recherche avancée")
  public Response rechercherCotisations(
      @QueryParam("membreId") UUID membreId,
      @QueryParam("statut") String statut,
      @QueryParam("typeCotisation") String typeCotisation,
      @QueryParam("annee") Integer annee,
      @QueryParam("mois") Integer mois,
      @QueryParam("page") @DefaultValue("0") int page,
      @QueryParam("size") @DefaultValue("20") int size) {
    try {
      List<CotisationResponse> results = cotisationService.rechercherCotisations(
          membreId, statut, typeCotisation, annee, mois, page, size);
      return Response.ok(results).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Statistiques globales (alias /stats pour le client).
   */
  @GET
  @Path("/stats")
  @Operation(summary = "Statistiques globales")
  public Response getStatistiquesCotisationsStats() {
    try {
      return Response.ok(cotisationService.getStatistiquesCotisations()).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Statistiques globales (chemin alternatif).
   */
  @GET
  @Path("/statistiques")
  @Operation(summary = "Statistiques globales")
  public Response getStatistiquesCotisations() {
    try {
      return Response.ok(cotisationService.getStatistiquesCotisations()).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Statistiques par période.
   */
  @GET
  @Path("/statistiques/periode")
  @Operation(summary = "Statistiques par période")
  public Response getStatistiquesPeriode(@QueryParam("annee") int annee, @QueryParam("mois") Integer mois) {
    try {
      return Response.ok(cotisationService.getStatistiquesPeriode(annee, mois)).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Enregistrer le paiement.
   */
  @PUT
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "MEMBRE", "TRESORIER" })
  @Path("/{id}/payer")
  @Operation(summary = "Payer une cotisation")
  public Response enregistrerPaiement(@PathParam("id") UUID id, Map<String, Object> paiementData) {
    try {
      BigDecimal montantPaye = paiementData.get("montantPaye") != null
          ? new BigDecimal(paiementData.get("montantPaye").toString())
          : null;
      LocalDate datePaiement = paiementData.get("datePaiement") != null
          ? LocalDate.parse(paiementData.get("datePaiement").toString())
          : null;
      String modePaiement = paiementData.get("modePaiement") != null ? paiementData.get("modePaiement").toString()
          : null;
      String reference = paiementData.get("reference") != null ? paiementData.get("reference").toString() : null;

      CotisationResponse result = cotisationService.enregistrerPaiement(id, montantPaye, datePaiement, modePaiement,
          reference);
      return Response.ok(result).build();
    } catch (NotFoundException e) {
      return Response.status(Response.Status.NOT_FOUND).entity(Map.of("error", e.getMessage())).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Envoyer rappels groupés.
   */
  @POST
  @RolesAllowed({ "ADMIN", "ADMIN_ORGANISATION", "MEMBRE" })
  @Path("/rappels/groupes")
  @Operation(summary = "Rappels groupés")
  public Response envoyerRappelsGroupes(List<UUID> membreIds) {
    try {
      int rappels = cotisationService.envoyerRappelsCotisationsGroupes(membreIds);
      return Response.ok(Map.of("rappelsEnvoyes", rappels)).build();
    } catch (Exception e) {
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(Map.of("error", e.getMessage())).build();
    }
  }

  /**
   * Toutes les cotisations du membre connecté (tous statuts).
   * Permet d'alimenter les onglets Toutes / Payées / Dues / Retard.
   */
  @GET
  @Path("/mes-cotisations")
  @RolesAllowed({ "MEMBRE", "ADMIN", "ADMIN_ORGANISATION" })
  @Operation(summary = "Mes cotisations", description = "Liste toutes les cotisations du membre connecté")
  @APIResponse(responseCode = "200", description = "Liste récupérée")
  public Response getMesCotisations(
      @QueryParam("page") @DefaultValue("0") int page,
      @QueryParam("size") @DefaultValue("50") int size) {
    try {
      log.info("GET /api/cotisations/mes-cotisations");
      List<CotisationResponse> results = cotisationService.getMesCotisations(page, size);
      return Response.ok(results).build();
    } catch (Exception e) {
      log.error("Erreur récupération mes cotisations", e);
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
          .entity(Map.of("error", "Erreur lors de la récupération de vos cotisations", "message", e.getMessage()))
          .build();
    }
  }

  /**
   * Liste les cotisations en attente du membre connecté.
   * Auto-détection du membre via SecurityIdentity (pas de membreId en paramètre).
   *
   * @return Liste des cotisations en attente
   */
  @GET
  @Path("/mes-cotisations/en-attente")
  @RolesAllowed({ "MEMBRE", "ADMIN", "ADMIN_ORGANISATION" })
  @Operation(summary = "Mes cotisations en attente", description = "Cotisations personnelles en attente de paiement")
  @APIResponse(responseCode = "200", description = "Liste récupérée")
  public Response getMesCotisationsEnAttente() {
    try {
      log.info("GET /api/cotisations/mes-cotisations/en-attente");
      List<CotisationResponse> results = cotisationService.getMesCotisationsEnAttente();
      return Response.ok(results).build();
    } catch (Exception e) {
      log.error("Erreur récupération mes cotisations en attente", e);
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
          .entity(Map.of("error", "Erreur lors de la récupération de vos cotisations", "message", e.getMessage()))
          .build();
    }
  }

  /**
   * Récupère la synthèse des cotisations personnelles du membre connecté.
   * Auto-détection du membre via SecurityIdentity.
   *
   * @return Synthèse (KPI personnels)
   */
  @GET
  @Path("/mes-cotisations/synthese")
  @RolesAllowed({ "MEMBRE", "ADMIN", "ADMIN_ORGANISATION" })
  @Operation(summary = "Synthèse de mes cotisations", description = "KPI personnels : cotisations à payer, montant dû, etc.")
  @APIResponse(responseCode = "200", description = "Synthèse récupérée")
  public Response getMesCotisationsSynthese() {
    try {
      log.info("GET /api/cotisations/mes-cotisations/synthese");
      Map<String, Object> synthese = cotisationService.getMesCotisationsSynthese();
      return Response.ok(synthese).build();
    } catch (Exception e) {
      log.error("Erreur récupération synthèse cotisations personnelles", e);
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
          .entity(Map.of("error", "Erreur lors de la récupération de votre synthèse", "message", e.getMessage()))
          .build();
    }
  }
}
