package dev.lions.user.manager.resource;

import dev.lions.user.manager.dto.audit.AuditLogDTO;
import dev.lions.user.manager.enums.audit.TypeActionAudit;
import dev.lions.user.manager.service.AuditService;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.validation.constraints.NotBlank;
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.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * REST Resource pour l'audit et la consultation des logs
 */
@Path("/api/audit")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Audit", description = "Consultation des logs d'audit et statistiques")
@Slf4j
public class AuditResource {

    @Inject
    AuditService auditService;

    @POST
    @Path("/search")
    @Operation(summary = "Rechercher des logs d'audit", description = "Recherche avancée de logs selon critères")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Résultats de recherche"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "auditor"})
    public Response searchLogs(
        @QueryParam("acteur") String acteurUsername,
        @QueryParam("dateDebut") String dateDebutStr,
        @QueryParam("dateFin") String dateFinStr,
        @QueryParam("typeAction") TypeActionAudit typeAction,
        @QueryParam("ressourceType") String ressourceType,
        @QueryParam("succes") Boolean succes,
        @QueryParam("page") @DefaultValue("0") int page,
        @QueryParam("pageSize") @DefaultValue("50") int pageSize
    ) {
        log.info("POST /api/audit/search - Recherche de logs");

        try {
            LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
            LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;

            // Utiliser findByActeur si acteurUsername est fourni, sinon findByRealm
            List<AuditLogDTO> logs;
            if (acteurUsername != null && !acteurUsername.isBlank()) {
                logs = auditService.findByActeur(acteurUsername, dateDebut, dateFin, page, pageSize);
            } else {
                // Pour une recherche générale, utiliser findByRealm (on utilise "master" par défaut)
                logs = auditService.findByRealm("master", dateDebut, dateFin, page, pageSize);
            }
            
            // Filtrer par typeAction, ressourceType et succes si fournis
            if (typeAction != null || ressourceType != null || succes != null) {
                logs = logs.stream()
                    .filter(log -> typeAction == null || typeAction.equals(log.getTypeAction()))
                    .filter(log -> ressourceType == null || ressourceType.equals(log.getRessourceType()))
                    .filter(log -> succes == null || succes == log.isSuccessful())
                    .collect(java.util.stream.Collectors.toList());
            }

            return Response.ok(logs).build();
        } catch (Exception e) {
            log.error("Erreur lors de la recherche de logs d'audit", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/actor/{acteurUsername}")
    @Operation(summary = "Récupérer les logs d'un acteur", description = "Liste les derniers logs d'un utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des logs"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "auditor"})
    public Response getLogsByActor(
        @Parameter(description = "Username de l'acteur") @PathParam("acteurUsername") @NotBlank String acteurUsername,
        @Parameter(description = "Nombre de logs à retourner") @QueryParam("limit") @DefaultValue("100") int limit
    ) {
        log.info("GET /api/audit/actor/{} - Limite: {}", acteurUsername, limit);

        try {
            List<AuditLogDTO> logs = auditService.findByActeur(acteurUsername, null, null, 0, limit);
            return Response.ok(logs).build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des logs de l'acteur {}", acteurUsername, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/resource/{ressourceType}/{ressourceId}")
    @Operation(summary = "Récupérer les logs d'une ressource", description = "Liste les derniers logs d'une ressource spécifique")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des logs"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "auditor"})
    public Response getLogsByResource(
        @PathParam("ressourceType") @NotBlank String ressourceType,
        @PathParam("ressourceId") @NotBlank String ressourceId,
        @QueryParam("limit") @DefaultValue("100") int limit
    ) {
        log.info("GET /api/audit/resource/{}/{} - Limite: {}", ressourceType, ressourceId, limit);

        try {
            List<AuditLogDTO> logs = auditService.findByRessource(ressourceType, ressourceId, null, null, 0, limit);
            return Response.ok(logs).build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des logs de la ressource {}:{}",
                     ressourceType, ressourceId, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/action/{typeAction}")
    @Operation(summary = "Récupérer les logs par type d'action", description = "Liste les logs d'un type d'action spécifique")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Liste des logs"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "auditor"})
    public Response getLogsByAction(
        @PathParam("typeAction") TypeActionAudit typeAction,
        @QueryParam("dateDebut") String dateDebutStr,
        @QueryParam("dateFin") String dateFinStr,
        @QueryParam("limit") @DefaultValue("100") int limit
    ) {
        log.info("GET /api/audit/action/{} - Limite: {}", typeAction, limit);

        try {
            LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
            LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;

            List<AuditLogDTO> logs = auditService.findByTypeAction(typeAction, "master", dateDebut, dateFin, 0, limit);
            return Response.ok(logs).build();
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des logs de type {}", typeAction, e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/stats/actions")
    @Operation(summary = "Statistiques par type d'action", description = "Retourne le nombre de logs par type d'action")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Statistiques des actions"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "auditor"})
    public Response getActionStatistics(
        @QueryParam("dateDebut") String dateDebutStr,
        @QueryParam("dateFin") String dateFinStr
    ) {
        log.info("GET /api/audit/stats/actions - Période: {} à {}", dateDebutStr, dateFinStr);

        try {
            LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
            LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;

            Map<TypeActionAudit, Long> stats = auditService.countByActionType("master", dateDebut, dateFin);
            return Response.ok(stats).build();
        } catch (Exception e) {
            log.error("Erreur lors du calcul des statistiques d'actions", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/stats/users")
    @Operation(summary = "Statistiques par utilisateur", description = "Retourne le nombre d'actions par utilisateur")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Statistiques des utilisateurs"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "auditor"})
    public Response getUserActivityStatistics(
        @QueryParam("dateDebut") String dateDebutStr,
        @QueryParam("dateFin") String dateFinStr
    ) {
        log.info("GET /api/audit/stats/users - Période: {} à {}", dateDebutStr, dateFinStr);

        try {
            LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
            LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;

            Map<String, Long> stats = auditService.countByActeur("master", dateDebut, dateFin);
            return Response.ok(stats).build();
        } catch (Exception e) {
            log.error("Erreur lors du calcul des statistiques utilisateurs", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/stats/failures")
    @Operation(summary = "Comptage des échecs", description = "Retourne le nombre d'échecs sur une période")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Nombre d'échecs"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "auditor"})
    public Response getFailureCount(
        @QueryParam("dateDebut") String dateDebutStr,
        @QueryParam("dateFin") String dateFinStr
    ) {
        log.info("GET /api/audit/stats/failures - Période: {} à {}", dateDebutStr, dateFinStr);

        try {
            LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
            LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;

            Map<String, Long> successVsFailure = auditService.countSuccessVsFailure("master", dateDebut, dateFin);
            long count = successVsFailure.getOrDefault("failure", 0L);
            return Response.ok(new CountResponse(count)).build();
        } catch (Exception e) {
            log.error("Erreur lors du comptage des échecs", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/stats/success")
    @Operation(summary = "Comptage des succès", description = "Retourne le nombre de succès sur une période")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Nombre de succès"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "auditor"})
    public Response getSuccessCount(
        @QueryParam("dateDebut") String dateDebutStr,
        @QueryParam("dateFin") String dateFinStr
    ) {
        log.info("GET /api/audit/stats/success - Période: {} à {}", dateDebutStr, dateFinStr);

        try {
            LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
            LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;

            Map<String, Long> successVsFailure = auditService.countSuccessVsFailure("master", dateDebut, dateFin);
            long count = successVsFailure.getOrDefault("success", 0L);
            return Response.ok(new CountResponse(count)).build();
        } catch (Exception e) {
            log.error("Erreur lors du comptage des succès", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @GET
    @Path("/export/csv")
    @Produces(MediaType.TEXT_PLAIN)
    @Operation(summary = "Exporter les logs en CSV", description = "Génère un fichier CSV des logs d'audit")
    @APIResponses({
        @APIResponse(responseCode = "200", description = "Fichier CSV généré"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin", "auditor"})
    public Response exportLogsToCSV(
        @QueryParam("dateDebut") String dateDebutStr,
        @QueryParam("dateFin") String dateFinStr
    ) {
        log.info("GET /api/audit/export/csv - Période: {} à {}", dateDebutStr, dateFinStr);

        try {
            LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
            LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;

            String csvContent = auditService.exportToCSV("master", dateDebut, dateFin);

            return Response.ok(csvContent)
                .header("Content-Disposition", "attachment; filename=\"audit-logs-" +
                        LocalDateTime.now().toString().replace(":", "-") + ".csv\"")
                .build();
        } catch (Exception e) {
            log.error("Erreur lors de l'export CSV des logs", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    @DELETE
    @Path("/purge")
    @Operation(summary = "Purger les anciens logs", description = "Supprime les logs de plus de X jours")
    @APIResponses({
        @APIResponse(responseCode = "204", description = "Purge effectuée"),
        @APIResponse(responseCode = "500", description = "Erreur serveur")
    })
    @RolesAllowed({"admin"})
    public Response purgeOldLogs(
        @QueryParam("joursAnciennete") @DefaultValue("90") int joursAnciennete
    ) {
        log.info("DELETE /api/audit/purge - Suppression des logs de plus de {} jours", joursAnciennete);

        try {
            LocalDateTime dateLimite = LocalDateTime.now().minusDays(joursAnciennete);
            auditService.purgeOldLogs(dateLimite);
            return Response.noContent().build();
        } catch (Exception e) {
            log.error("Erreur lors de la purge des logs", e);
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                .entity(new ErrorResponse(e.getMessage()))
                .build();
        }
    }

    // ==================== DTOs internes ====================

    @Schema(description = "Réponse de comptage")
    public static class CountResponse {
        @Schema(description = "Nombre d'éléments")
        public long count;

        public CountResponse(long count) {
            this.count = count;
        }
    }

    @Schema(description = "Réponse d'erreur")
    public static class ErrorResponse {
        @Schema(description = "Message d'erreur")
        public String message;

        public ErrorResponse(String message) {
            this.message = message;
        }
    }
}
