package handlers

import (
	"context"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
	"gorm.io/gorm"

	"veza-backend-api/internal/database"
	"veza-backend-api/internal/eventbus"
)

// HealthResponse représente la réponse du health check
type HealthResponse struct {
	Status    string                 `json:"status"`
	Timestamp string                 `json:"timestamp"`
	Checks    map[string]HealthCheck `json:"checks"`
}

// HealthCheck représente le résultat d'un check individuel
type HealthCheck struct {
	Status    string  `json:"status"`
	Message   string  `json:"message,omitempty"`
	Duration  float64 `json:"duration_ms,omitempty"`
	Threshold float64 `json:"threshold_ms,omitempty"`
}

// HealthHandler gère les health checks
type HealthHandler struct {
	db               *gorm.DB
	logger           *zap.Logger
	redis            *redis.Client              // Typé avec le vrai type Redis
	rabbitMQEventBus *eventbus.RabbitMQEventBus // Instance de l'EventBus RabbitMQ
}

// NewHealthHandler crée un nouveau handler de health
func NewHealthHandler(db *gorm.DB, logger *zap.Logger, redisClient interface{}, rabbitMQEventBus interface{}) *HealthHandler {
	h := &HealthHandler{
		db:     db,
		logger: logger,
	}

	// Type assertion for Redis
	if r, ok := redisClient.(*redis.Client); ok {
		h.redis = r
	}

	// Type assertion for RabbitMQ
	if eb, ok := rabbitMQEventBus.(*eventbus.RabbitMQEventBus); ok {
		h.rabbitMQEventBus = eb
	}

	return h
}

// NewHealthHandlerSimple crée un nouveau handler de health simple (sans logger/redis)
// Pour compatibilité avec la spécification T0012
func NewHealthHandlerSimple(db *gorm.DB) *HealthHandler {
	return &HealthHandler{
		db: db,
	}
}

// Check vérifie l'état de la base de données et retourne un status simple
// Cette méthode implémente la spécification T0012
// Route /health - Stateless, sans dépendances externes
func (h *HealthHandler) Check(c *gin.Context) {
	// Route /health simplifiée - toujours retourner {status: "ok"}
	// Stateless, sans vérification de dépendances
	c.JSON(http.StatusOK, gin.H{
		"status": "ok",
	})
}

// Health check endpoint (/health)
func (h *HealthHandler) Health(c *gin.Context) {
	response := HealthResponse{
		Status:    "ok",
		Timestamp: time.Now().UTC().Format(time.RFC3339),
		Checks:    make(map[string]HealthCheck),
	}

	// Check database
	dbCheck := h.checkDatabase()
	response.Checks["database"] = dbCheck

	// Check Redis
	redisCheck := h.checkRedis()
	response.Checks["redis"] = redisCheck

	// Check RabbitMQ
	rabbitMQCheck := h.checkRabbitMQ()
	response.Checks["rabbitmq"] = rabbitMQCheck

	// Déterminer le statut global
	for _, check := range response.Checks {
		if check.Status == "error" {
			response.Status = "degraded"
			break
		}
		if check.Status == "slow" {
			if response.Status != "degraded" {
				response.Status = "degraded"
			}
		}
	}

	statusCode := http.StatusOK
	if response.Status == "degraded" {
		statusCode = http.StatusServiceUnavailable
	}

	c.JSON(statusCode, response)
}

// Readiness check endpoint (/ready)
func (h *HealthHandler) Readiness(c *gin.Context) {
	response := HealthResponse{
		Status:    "ready",
		Timestamp: time.Now().UTC().Format(time.RFC3339),
		Checks:    make(map[string]HealthCheck),
	}

	// Vérifier que la DB est accessible
	dbCheck := h.checkDatabase()
	response.Checks["database"] = dbCheck

	// Vérifier que Redis est accessible
	redisCheck := h.checkRedis()
	response.Checks["redis"] = redisCheck

	// Vérifier que RabbitMQ est accessible (si activé)
	rabbitMQCheck := h.checkRabbitMQ()
	response.Checks["rabbitmq"] = rabbitMQCheck

	// Si un check est en erreur, on n'est pas ready
	for _, check := range response.Checks {
		if check.Status == "error" {
			response.Status = "not_ready"
			c.JSON(http.StatusServiceUnavailable, response)
			return
		}
	}

	c.JSON(http.StatusOK, response)
}

// Liveness check endpoint (/live)
func (h *HealthHandler) Liveness(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status":    "alive",
		"timestamp": time.Now().UTC().Format(time.RFC3339),
	})
}

// SimpleHealthCheck est une fonction simple pour le health check endpoint public
func SimpleHealthCheck(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status":  "healthy",
		"service": "veza-backend-api",
	})
}

// checkDatabase vérifie la connexion à la base de données avec pool stats
func (h *HealthHandler) checkDatabase() HealthCheck {
	start := time.Now()

	// Utiliser IsConnectionHealthy avec timeout de 5 secondes
	err := database.IsConnectionHealthy(h.db, 5*time.Second)
	duration := time.Since(start)

	if err != nil {
		return HealthCheck{
			Status:   "error",
			Message:  err.Error(),
			Duration: float64(duration.Nanoseconds()) / 1e6,
		}
	}

	threshold := 100.0 // 100ms threshold
	status := "ok"

	if duration.Milliseconds() > int64(threshold) {
		status = "slow"
	}

	// Récupérer les statistiques du pool
	poolStats, statsErr := database.GetPoolStats(h.db)
	var message string
	if statsErr == nil {
		message = "pool_connections"
		// On pourrait ajouter plus d'informations sur le pool ici
		_ = poolStats // Utiliser dans le futur pour plus de détails
	}

	return HealthCheck{
		Status:    status,
		Message:   message,
		Duration:  float64(duration.Nanoseconds()) / 1e6, // Convert to ms
		Threshold: threshold,
	}
}

// checkRedis vérifie la connexion à Redis
func (h *HealthHandler) checkRedis() HealthCheck {
	start := time.Now()
	threshold := 50.0 // 50ms threshold

	if h.redis == nil {
		return HealthCheck{
			Status:  "error",
			Message: "Redis connection not configured",
		}
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	_, err := h.redis.Ping(ctx).Result()
	duration := time.Since(start)

	if err != nil {
		return HealthCheck{
			Status:   "error",
			Message:  err.Error(),
			Duration: float64(duration.Nanoseconds()) / 1e6,
		}
	}

	status := "ok"
	if duration.Milliseconds() > int64(threshold) {
		status = "slow"
	}

	return HealthCheck{
		Status:    status,
		Duration:  float64(duration.Nanoseconds()) / 1e6,
		Threshold: threshold,
	}
}

// checkRabbitMQ vérifie la connexion à RabbitMQ (Event Bus)
func (h *HealthHandler) checkRabbitMQ() HealthCheck {
	start := time.Now()
	threshold := 100.0 // 100ms threshold

	// Vérifier si l'EventBus est configuré
	if h.rabbitMQEventBus == nil {
		return HealthCheck{
			Status:  "error",
			Message: "RabbitMQ EventBus not configured",
		}
	}

	// Vérifier si l'EventBus est activé via le champ booléen
	if !h.rabbitMQEventBus.IsEnabled {
		return HealthCheck{
			Status:  "disabled",
			Message: "RabbitMQ EventBus is disabled by configuration",
		}
	}

	// Tenter un Health Check réel
	if err := h.rabbitMQEventBus.Health(); err != nil {
		duration := time.Since(start)
		return HealthCheck{
			Status:   "error",
			Message:  err.Error(),
			Duration: float64(duration.Nanoseconds()) / 1e6,
		}
	}

	duration := time.Since(start)
	status := "ok"
	if duration.Milliseconds() > int64(threshold) {
		status = "slow"
	}

	return HealthCheck{
		Status:    status,
		Duration:  float64(duration.Nanoseconds()) / 1e6,
		Threshold: threshold,
	}
}
