package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.api.dto.finance.CotisationDTO;
import dev.lions.unionflow.server.service.CotisationService;
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 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;

import java.util.List;
import java.util.Map;

/**
 * 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")
@Slf4j
public class CotisationResource {

    @Inject
    CotisationService cotisationService;

    /**
     * Endpoint public pour les cotisations (test)
     */
    @GET
    @Path("/public")
    @Operation(summary = "Cotisations publiques", description = "Liste des cotisations sans authentification")
    @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 {
            System.out.println("GET /api/cotisations/public - page: " + page + ", size: " + size);

            // Données de test pour l'application mobile
            List<Map<String, Object>> cotisations = List.of(
                Map.of(
                    "id", "1",
                    "nom", "Cotisation Mensuelle Janvier 2025",
                    "description", "Cotisation mensuelle pour le mois de janvier",
                    "montant", 25000.0,
                    "devise", "XOF",
                    "dateEcheance", "2025-01-31T23:59:59",
                    "statut", "ACTIVE",
                    "type", "MENSUELLE"
                ),
                Map.of(
                    "id", "2",
                    "nom", "Cotisation Spéciale Projet",
                    "description", "Cotisation pour le financement du projet communautaire",
                    "montant", 50000.0,
                    "devise", "XOF",
                    "dateEcheance", "2025-03-15T23:59:59",
                    "statut", "ACTIVE",
                    "type", "SPECIALE"
                )
            );

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

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

        } catch (Exception e) {
            System.err.println("Erreur lors de la récupération des cotisations publiques: " + e.getMessage());
            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 de toutes les cotisations")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des cotisations récupérée avec succès",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON, 
                                     schema = @Schema(implementation = CotisationDTO.class))),
        @APIResponse(responseCode = "400", description = "Paramètres de pagination invalides"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response getAllCotisations(
            @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) {
        
        try {
            log.info("GET /api/cotisations - page: {}, size: {}", page, size);
            
            List<CotisationDTO> cotisations = cotisationService.getAllCotisations(page, size);
            
            log.info("Récupération réussie de {} cotisations", cotisations.size());
            return Response.ok(cotisations).build();
            
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des cotisations", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(Map.of("error", "Erreur lors de la récupération des cotisations", 
                                  "message", e.getMessage()))
                    .build();
        }
    }

    /**
     * Récupère une cotisation par son ID
     */
    @GET
    @Path("/{id}")
    @Operation(summary = "Récupérer une cotisation par ID", 
               description = "Récupère les détails d'une cotisation spécifique")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Cotisation trouvée",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON, 
                                     schema = @Schema(implementation = CotisationDTO.class))),
        @APIResponse(responseCode = "404", description = "Cotisation non trouvée"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response getCotisationById(
            @Parameter(description = "Identifiant de la cotisation", required = true)
            @PathParam("id") @NotNull Long id) {
        
        try {
            log.info("GET /api/cotisations/{}", id);
            
            CotisationDTO cotisation = cotisationService.getCotisationById(id);
            
            log.info("Cotisation récupérée avec succès - ID: {}", id);
            return Response.ok(cotisation).build();
            
        } catch (NotFoundException e) {
            log.warn("Cotisation non trouvée - ID: {}", id);
            return Response.status(Response.Status.NOT_FOUND)
                    .entity(Map.of("error", "Cotisation non trouvée", "id", id))
                    .build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération de la cotisation - ID: " + id, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(Map.of("error", "Erreur lors de la récupération de la cotisation", 
                                  "message", e.getMessage()))
                    .build();
        }
    }

    /**
     * Récupère une cotisation par son numéro de référence
     */
    @GET
    @Path("/reference/{numeroReference}")
    @Operation(summary = "Récupérer une cotisation par référence", 
               description = "Récupère une cotisation par son numéro de référence unique")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Cotisation trouvée"),
        @APIResponse(responseCode = "404", description = "Cotisation non trouvée"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response getCotisationByReference(
            @Parameter(description = "Numéro de référence de la cotisation", required = true)
            @PathParam("numeroReference") @NotNull String numeroReference) {
        
        try {
            log.info("GET /api/cotisations/reference/{}", numeroReference);
            
            CotisationDTO cotisation = cotisationService.getCotisationByReference(numeroReference);
            
            log.info("Cotisation récupérée avec succès - Référence: {}", numeroReference);
            return Response.ok(cotisation).build();
            
        } catch (NotFoundException e) {
            log.warn("Cotisation non trouvée - Référence: {}", numeroReference);
            return Response.status(Response.Status.NOT_FOUND)
                    .entity(Map.of("error", "Cotisation non trouvée", "reference", numeroReference))
                    .build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération de la cotisation - Référence: " + numeroReference, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(Map.of("error", "Erreur lors de la récupération de la cotisation", 
                                  "message", e.getMessage()))
                    .build();
        }
    }

    /**
     * Crée une nouvelle cotisation
     */
    @POST
    @Operation(summary = "Créer une nouvelle cotisation", 
               description = "Crée une nouvelle cotisation pour un membre")
    @APIResponses({
        @APIResponse(responseCode = "201", description = "Cotisation créée avec succès",
                    content = @Content(mediaType = MediaType.APPLICATION_JSON, 
                                     schema = @Schema(implementation = CotisationDTO.class))),
        @APIResponse(responseCode = "400", description = "Données invalides"),
        @APIResponse(responseCode = "404", description = "Membre non trouvé"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response createCotisation(
            @Parameter(description = "Données de la cotisation à créer", required = true)
            @Valid CotisationDTO cotisationDTO) {
        
        try {
            log.info("POST /api/cotisations - Création cotisation pour membre: {}", 
                    cotisationDTO.getMembreId());
            
            CotisationDTO nouvelleCotisation = cotisationService.createCotisation(cotisationDTO);
            
            log.info("Cotisation créée avec succès - ID: {}, Référence: {}", 
                    nouvelleCotisation.getId(), nouvelleCotisation.getNumeroReference());
            
            return Response.status(Response.Status.CREATED)
                    .entity(nouvelleCotisation)
                    .build();
            
        } catch (NotFoundException e) {
            log.warn("Membre non trouvé lors de la création de cotisation: {}", cotisationDTO.getMembreId());
            return Response.status(Response.Status.NOT_FOUND)
                    .entity(Map.of("error", "Membre non trouvé", "membreId", cotisationDTO.getMembreId()))
                    .build();
        } catch (IllegalArgumentException e) {
            log.warn("Données invalides pour la création de cotisation: {}", e.getMessage());
            return Response.status(Response.Status.BAD_REQUEST)
                    .entity(Map.of("error", "Données invalides", "message", e.getMessage()))
                    .build();
        } catch (Exception e) {
            log.error("Erreur lors de la création de la cotisation", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(Map.of("error", "Erreur lors de la création de la cotisation", 
                                  "message", e.getMessage()))
                    .build();
        }
    }

    /**
     * Met à jour une cotisation existante
     */
    @PUT
    @Path("/{id}")
    @Operation(summary = "Mettre à jour une cotisation", 
               description = "Met à jour les données d'une cotisation existante")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Cotisation mise à jour avec succès"),
        @APIResponse(responseCode = "400", description = "Données invalides"),
        @APIResponse(responseCode = "404", description = "Cotisation non trouvée"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response updateCotisation(
            @Parameter(description = "Identifiant de la cotisation", required = true)
            @PathParam("id") @NotNull Long id,
            
            @Parameter(description = "Nouvelles données de la cotisation", required = true)
            @Valid CotisationDTO cotisationDTO) {
        
        try {
            log.info("PUT /api/cotisations/{}", id);
            
            CotisationDTO cotisationMiseAJour = cotisationService.updateCotisation(id, cotisationDTO);
            
            log.info("Cotisation mise à jour avec succès - ID: {}", id);
            return Response.ok(cotisationMiseAJour).build();
            
        } catch (NotFoundException e) {
            log.warn("Cotisation non trouvée pour mise à jour - ID: {}", id);
            return Response.status(Response.Status.NOT_FOUND)
                    .entity(Map.of("error", "Cotisation non trouvée", "id", id))
                    .build();
        } catch (IllegalArgumentException e) {
            log.warn("Données invalides pour la mise à jour de cotisation - ID: {}, Erreur: {}", id, e.getMessage());
            return Response.status(Response.Status.BAD_REQUEST)
                    .entity(Map.of("error", "Données invalides", "message", e.getMessage()))
                    .build();
        } catch (Exception e) {
            log.error("Erreur lors de la mise à jour de la cotisation - ID: " + id, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(Map.of("error", "Erreur lors de la mise à jour de la cotisation", 
                                  "message", e.getMessage()))
                    .build();
        }
    }

    /**
     * Supprime une cotisation
     */
    @DELETE
    @Path("/{id}")
    @Operation(summary = "Supprimer une cotisation", 
               description = "Supprime (désactive) une cotisation")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Cotisation supprimée avec succès"),
        @APIResponse(responseCode = "404", description = "Cotisation non trouvée"),
        @APIResponse(responseCode = "409", description = "Impossible de supprimer une cotisation payée"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response deleteCotisation(
            @Parameter(description = "Identifiant de la cotisation", required = true)
            @PathParam("id") @NotNull Long id) {
        
        try {
            log.info("DELETE /api/cotisations/{}", id);
            
            cotisationService.deleteCotisation(id);
            
            log.info("Cotisation supprimée avec succès - ID: {}", id);
            return Response.noContent().build();
            
        } catch (NotFoundException e) {
            log.warn("Cotisation non trouvée pour suppression - ID: {}", id);
            return Response.status(Response.Status.NOT_FOUND)
                    .entity(Map.of("error", "Cotisation non trouvée", "id", id))
                    .build();
        } catch (IllegalStateException e) {
            log.warn("Impossible de supprimer la cotisation - ID: {}, Raison: {}", id, e.getMessage());
            return Response.status(Response.Status.CONFLICT)
                    .entity(Map.of("error", "Impossible de supprimer la cotisation", "message", e.getMessage()))
                    .build();
        } catch (Exception e) {
            log.error("Erreur lors de la suppression de la cotisation - ID: " + id, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(Map.of("error", "Erreur lors de la suppression de la cotisation", 
                                  "message", e.getMessage()))
                    .build();
        }
    }

    /**
     * Récupère les cotisations d'un membre
     */
    @GET
    @Path("/membre/{membreId}")
    @Operation(summary = "Lister les cotisations d'un membre", 
               description = "Récupère toutes les cotisations d'un membre spécifique")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des cotisations du membre"),
        @APIResponse(responseCode = "404", description = "Membre non trouvé"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response getCotisationsByMembre(
            @Parameter(description = "Identifiant du membre", required = true)
            @PathParam("membreId") @NotNull Long 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) {
        
        try {
            log.info("GET /api/cotisations/membre/{} - page: {}, size: {}", membreId, page, size);
            
            List<CotisationDTO> cotisations = cotisationService.getCotisationsByMembre(membreId, page, size);
            
            log.info("Récupération réussie de {} cotisations pour le membre {}", cotisations.size(), membreId);
            return Response.ok(cotisations).build();
            
        } catch (NotFoundException e) {
            log.warn("Membre non trouvé - ID: {}", membreId);
            return Response.status(Response.Status.NOT_FOUND)
                    .entity(Map.of("error", "Membre non trouvé", "membreId", membreId))
                    .build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des cotisations du membre - ID: " + membreId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(Map.of("error", "Erreur lors de la récupération des cotisations",
                                  "message", e.getMessage()))
                    .build();
        }
    }

    /**
     * Récupère les cotisations par statut
     */
    @GET
    @Path("/statut/{statut}")
    @Operation(summary = "Lister les cotisations par statut",
               description = "Récupère toutes les cotisations ayant un statut spécifique")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des cotisations avec le statut spécifié"),
        @APIResponse(responseCode = "400", description = "Statut invalide"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response getCotisationsByStatut(
            @Parameter(description = "Statut des cotisations", 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) {

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

            List<CotisationDTO> cotisations = cotisationService.getCotisationsByStatut(statut, page, size);

            log.info("Récupération réussie de {} cotisations avec statut {}", cotisations.size(), statut);
            return Response.ok(cotisations).build();

        } catch (Exception e) {
            log.error("Erreur lors de la récupération des cotisations par statut - Statut: " + statut, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(Map.of("error", "Erreur lors de la récupération des cotisations",
                                  "message", e.getMessage()))
                    .build();
        }
    }

    /**
     * Récupère les cotisations en retard
     */
    @GET
    @Path("/en-retard")
    @Operation(summary = "Lister les cotisations en retard",
               description = "Récupère toutes les cotisations dont la date d'échéance est dépassée")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des cotisations en retard"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response getCotisationsEnRetard(
            @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) {

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

            List<CotisationDTO> cotisations = cotisationService.getCotisationsEnRetard(page, size);

            log.info("Récupération réussie de {} cotisations en retard", cotisations.size());
            return Response.ok(cotisations).build();

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

    /**
     * Recherche avancée de cotisations
     */
    @GET
    @Path("/recherche")
    @Operation(summary = "Recherche avancée de cotisations",
               description = "Recherche de cotisations avec filtres multiples")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Résultats de la recherche"),
        @APIResponse(responseCode = "400", description = "Paramètres de recherche invalides"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response rechercherCotisations(
            @Parameter(description = "Identifiant du membre")
            @QueryParam("membreId") Long membreId,

            @Parameter(description = "Statut de la cotisation")
            @QueryParam("statut") String statut,

            @Parameter(description = "Type de cotisation")
            @QueryParam("typeCotisation") String typeCotisation,

            @Parameter(description = "Année")
            @QueryParam("annee") Integer annee,

            @Parameter(description = "Mois")
            @QueryParam("mois") Integer mois,

            @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) {

        try {
            log.info("GET /api/cotisations/recherche - Filtres: membreId={}, statut={}, type={}, annee={}, mois={}",
                    membreId, statut, typeCotisation, annee, mois);

            List<CotisationDTO> cotisations = cotisationService.rechercherCotisations(
                    membreId, statut, typeCotisation, annee, mois, page, size);

            log.info("Recherche réussie - {} cotisations trouvées", cotisations.size());
            return Response.ok(cotisations).build();

        } catch (Exception e) {
            log.error("Erreur lors de la recherche de cotisations", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(Map.of("error", "Erreur lors de la recherche de cotisations",
                                  "message", e.getMessage()))
                    .build();
        }
    }

    /**
     * Récupère les statistiques des cotisations
     */
    @GET
    @Path("/stats")
    @Operation(summary = "Statistiques des cotisations",
               description = "Récupère les statistiques globales des cotisations")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Statistiques récupérées avec succès"),
        @APIResponse(responseCode = "500", description = "Erreur interne du serveur")
    })
    public Response getStatistiquesCotisations() {
        try {
            log.info("GET /api/cotisations/stats");

            Map<String, Object> statistiques = cotisationService.getStatistiquesCotisations();

            log.info("Statistiques récupérées avec succès");
            return Response.ok(statistiques).build();

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