package dev.lions.unionflow.server.resource;

import dev.lions.unionflow.server.api.dto.analytics.AnalyticsDataResponse;
import dev.lions.unionflow.server.api.dto.analytics.DashboardWidgetResponse;
import dev.lions.unionflow.server.api.dto.analytics.KPITrendResponse;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import dev.lions.unionflow.server.service.AnalyticsService;
import dev.lions.unionflow.server.service.KPICalculatorService;
import io.quarkus.security.Authenticated;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
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.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;

/**
 * Ressource REST pour les analytics et métriques UnionFlow
 *
 * <p>Cette ressource expose les APIs pour accéder aux données analytics, KPI, tendances et widgets
 * de tableau de bord.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@Path("/api/v1/analytics")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Authenticated
@Tag(name = "Analytics", description = "APIs pour les analytics et métriques")
public class AnalyticsResource {

  private static final Logger log = Logger.getLogger(AnalyticsResource.class);

  @Inject AnalyticsService analyticsService;

  @Inject KPICalculatorService kpiCalculatorService;

  /** Calcule une métrique analytics pour une période donnée */
  @GET
  @Path("/metriques/{typeMetrique}")
  @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"})
  @Operation(
      summary = "Calculer une métrique analytics",
      description = "Calcule une métrique spécifique pour une période et organisation données")
  @APIResponse(responseCode = "200", description = "Métrique calculée avec succès")
  @APIResponse(responseCode = "400", description = "Paramètres invalides")
  @APIResponse(responseCode = "403", description = "Accès non autorisé")
  public Response calculerMetrique(
      @Parameter(description = "Type de métrique à calculer", required = true)
          @PathParam("typeMetrique")
          TypeMetrique typeMetrique,
      @Parameter(description = "Période d'analyse", required = true) @QueryParam("periode") @NotNull
          PeriodeAnalyse periodeAnalyse,
      @Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
          UUID organisationId) {

    try {
      log.infof(
          "Calcul de la métrique %s pour la période %s et l'organisation %s",
          typeMetrique, periodeAnalyse, organisationId);

      AnalyticsDataResponse result =
          analyticsService.calculerMetrique(typeMetrique, periodeAnalyse, organisationId);

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

    } catch (Exception e) {
      log.errorf(e, "Erreur lors du calcul de la métrique %s: %s", typeMetrique, e.getMessage());
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
          .entity(
              Map.of("error", "Erreur lors du calcul de la métrique", "message", e.getMessage()))
          .build();
    }
  }

  /** Calcule les tendances d'un KPI sur une période */
  @GET
  @Path("/tendances/{typeMetrique}")
  @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"})
  @Operation(
      summary = "Calculer la tendance d'un KPI",
      description = "Calcule l'évolution et les tendances d'un KPI sur une période donnée")
  @APIResponse(responseCode = "200", description = "Tendance calculée avec succès")
  @APIResponse(responseCode = "400", description = "Paramètres invalides")
  @APIResponse(responseCode = "403", description = "Accès non autorisé")
  public Response calculerTendanceKPI(
      @Parameter(description = "Type de métrique pour la tendance", required = true)
          @PathParam("typeMetrique")
          TypeMetrique typeMetrique,
      @Parameter(description = "Période d'analyse", required = true) @QueryParam("periode") @NotNull
          PeriodeAnalyse periodeAnalyse,
      @Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
          UUID organisationId) {

    try {
      log.infof(
          "Calcul de la tendance KPI %s pour la période %s et l'organisation %s",
          typeMetrique, periodeAnalyse, organisationId);

      KPITrendResponse result =
          analyticsService.calculerTendanceKPI(typeMetrique, periodeAnalyse, organisationId);

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

    } catch (Exception e) {
      log.errorf(
          e, "Erreur lors du calcul de la tendance KPI %s: %s", typeMetrique, e.getMessage());
      return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
          .entity(
              Map.of("error", "Erreur lors du calcul de la tendance", "message", e.getMessage()))
          .build();
    }
  }

  /** Obtient tous les KPI pour une organisation */
  @GET
  @Path("/kpis")
  @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"})
  @Operation(
      summary = "Obtenir tous les KPI",
      description = "Récupère tous les KPI calculés pour une organisation et période données")
  @APIResponse(responseCode = "200", description = "KPI récupérés avec succès")
  @APIResponse(responseCode = "400", description = "Paramètres invalides")
  @APIResponse(responseCode = "403", description = "Accès non autorisé")
  public Response obtenirTousLesKPI(
      @Parameter(description = "Période d'analyse", required = true) @QueryParam("periode") @NotNull
          PeriodeAnalyse periodeAnalyse,
      @Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
          UUID organisationId) {

    try {
      log.infof(
          "Récupération de tous les KPI pour la période %s et l'organisation %s",
          periodeAnalyse, organisationId);

      Map<TypeMetrique, BigDecimal> kpis =
          kpiCalculatorService.calculerTousLesKPI(
              organisationId, periodeAnalyse.getDateDebut(), periodeAnalyse.getDateFin());

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

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

  /** Calcule le KPI de performance globale */
  @GET
  @Path("/performance-globale")
  @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "SUPER_ADMIN"})
  @Operation(
      summary = "Calculer la performance globale",
      description = "Calcule le score de performance globale de l'organisation")
  @APIResponse(responseCode = "200", description = "Performance globale calculée avec succès")
  @APIResponse(responseCode = "403", description = "Accès non autorisé")
  public Response calculerPerformanceGlobale(
      @Parameter(description = "Période d'analyse", required = true) @QueryParam("periode") @NotNull
          PeriodeAnalyse periodeAnalyse,
      @Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
          UUID organisationId) {

    try {
      log.infof(
          "Calcul de la performance globale pour la période %s et l'organisation %s",
          periodeAnalyse, organisationId);

      BigDecimal performanceGlobale =
          kpiCalculatorService.calculerKPIPerformanceGlobale(
              organisationId, periodeAnalyse.getDateDebut(), periodeAnalyse.getDateFin());

      return Response.ok(
              Map.of(
                  "performanceGlobale", performanceGlobale,
                  "periode", periodeAnalyse,
                  "organisationId", organisationId,
                  "dateCalcul", java.time.LocalDateTime.now()))
          .build();

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

  /** Obtient les évolutions des KPI par rapport à la période précédente */
  @GET
  @Path("/evolutions")
  @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"})
  @Operation(
      summary = "Obtenir les évolutions des KPI",
      description = "Récupère les évolutions des KPI par rapport à la période précédente")
  @APIResponse(responseCode = "200", description = "Évolutions récupérées avec succès")
  @APIResponse(responseCode = "400", description = "Paramètres invalides")
  @APIResponse(responseCode = "403", description = "Accès non autorisé")
  public Response obtenirEvolutionsKPI(
      @Parameter(description = "Période d'analyse", required = true) @QueryParam("periode") @NotNull
          PeriodeAnalyse periodeAnalyse,
      @Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
          UUID organisationId) {

    try {
      log.infof(
          "Récupération des évolutions KPI pour la période %s et l'organisation %s",
          periodeAnalyse, organisationId);

      Map<TypeMetrique, BigDecimal> evolutions =
          kpiCalculatorService.calculerEvolutionsKPI(
              organisationId, periodeAnalyse.getDateDebut(), periodeAnalyse.getDateFin());

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

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

  /** Obtient les widgets du tableau de bord pour un utilisateur */
  @GET
  @Path("/dashboard/widgets")
  @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"})
  @Operation(
      summary = "Obtenir les widgets du tableau de bord",
      description = "Récupère tous les widgets configurés pour le tableau de bord de l'utilisateur")
  @APIResponse(responseCode = "200", description = "Widgets récupérés avec succès")
  @APIResponse(responseCode = "403", description = "Accès non autorisé")
  public Response obtenirWidgetsTableauBord(
      @Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
          UUID organisationId,
      @Parameter(description = "ID de l'utilisateur", required = true)
          @QueryParam("utilisateurId")
          @NotNull
          UUID utilisateurId) {

    try {
      log.infof(
          "Récupération des widgets du tableau de bord pour l'organisation %s et l'utilisateur %s",
          organisationId, utilisateurId);

      List<DashboardWidgetResponse> widgets =
          analyticsService.obtenirMetriquesTableauBord(organisationId, utilisateurId);

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

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

  /** Obtient les types de métriques disponibles */
  @GET
  @Path("/types-metriques")
  @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"})
  @Operation(
      summary = "Obtenir les types de métriques disponibles",
      description = "Récupère la liste de tous les types de métriques disponibles")
  @APIResponse(responseCode = "200", description = "Types de métriques récupérés avec succès")
  public Response obtenirTypesMetriques() {
    log.info("Récupération des types de métriques disponibles");
    TypeMetrique[] typesMetriques = TypeMetrique.values();
    return Response.ok(Map.of("typesMetriques", typesMetriques, "total", typesMetriques.length))
        .build();
  }

  /** Obtient les périodes d'analyse disponibles */
  @GET
  @Path("/periodes-analyse")
  @RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"})
  @Operation(
      summary = "Obtenir les périodes d'analyse disponibles",
      description = "Récupère la liste de toutes les périodes d'analyse disponibles")
  @APIResponse(responseCode = "200", description = "Périodes d'analyse récupérées avec succès")
  public Response obtenirPeriodesAnalyse() {
    log.info("Récupération des périodes d'analyse disponibles");
    PeriodeAnalyse[] periodesAnalyse = PeriodeAnalyse.values();
    return Response.ok(Map.of("periodesAnalyse", periodesAnalyse, "total", periodesAnalyse.length))
        .build();
  }
}
