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"`
	Message   string                 `json:"message,omitempty"` // MOD-P1-006: Added for degraded status message
}

// 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"`
	Details   map[string]interface{} `json:"details,omitempty"` // BE-SVC-016: Additional details
}

// 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
	environment      string                     // Env (development, production, etc.)
	s3Service        interface{}                // BE-SVC-016: S3 storage service (optional)
	jobWorker        interface{}                // BE-SVC-016: Job worker (optional)
	emailSender      interface{}                // BE-SVC-016: Email sender (optional)
}

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

	// 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
}

// NewHealthHandlerWithServices crée un nouveau handler de health avec services additionnels (BE-SVC-016)
func NewHealthHandlerWithServices(
	db *gorm.DB,
	logger *zap.Logger,
	redisClient interface{},
	rabbitMQEventBus interface{},
	env string,
	s3Service interface{},
	jobWorker interface{},
	emailSender interface{},
) *HealthHandler {
	h := NewHealthHandler(db, logger, redisClient, rabbitMQEventBus, env)
	h.s3Service = s3Service
	h.jobWorker = jobWorker
	h.emailSender = emailSender
	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
	RespondSuccess(c, 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

	// BE-SVC-016: Check S3 storage (if enabled)
	if h.s3Service != nil {
		s3Check := h.checkS3()
		response.Checks["s3_storage"] = s3Check
	}

	// BE-SVC-016: Check Job Worker (if enabled)
	if h.jobWorker != nil {
		jobWorkerCheck := h.checkJobWorker()
		response.Checks["job_worker"] = jobWorkerCheck
	}

	// BE-SVC-016: Check Email Sender (if enabled)
	if h.emailSender != nil {
		emailCheck := h.checkEmailSender()
		response.Checks["email_sender"] = emailCheck
	}

	// 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
	}

	RespondSuccess(c, 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

	// DB is critical in ALL environments
	if dbCheck.Status == "error" {
		response.Status = "not_ready"
		c.JSON(http.StatusServiceUnavailable, response)
		return
	}

	// MOD-P1-006: Redis and RabbitMQ are optional services
	// DB is critical, but Redis/RabbitMQ can be down (degraded mode)
	// 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

	// BE-SVC-016: Check optional services
	if h.s3Service != nil {
		s3Check := h.checkS3()
		response.Checks["s3_storage"] = s3Check
	}

	if h.jobWorker != nil {
		jobWorkerCheck := h.checkJobWorker()
		response.Checks["job_worker"] = jobWorkerCheck
	}

	if h.emailSender != nil {
		emailCheck := h.checkEmailSender()
		response.Checks["email_sender"] = emailCheck
	}

	// MOD-P1-006: Determine overall status
	// - DB error = not_ready (critical service)
	// - Redis/RabbitMQ error = degraded (optional services)
	// - All OK = ready
	hasOptionalServiceError := false
	for key, check := range response.Checks {
		// Skip database check (already handled above)
		if key == "database" {
			continue
		}
		if check.Status == "error" {
			hasOptionalServiceError = true
			break
		}
	}

	if hasOptionalServiceError {
		response.Status = "degraded"
		response.Message = "Service is operational but some optional services are unavailable"
		// MOD-P1-006: Log degraded status at warn level
		if h.logger != nil {
			h.logger.Warn("Readiness probe: degraded mode",
				zap.String("status", "degraded"),
				zap.Any("checks", response.Checks),
			)
		}
	} else {
		response.Status = "ready"
	}

	// MOD-P1-006: Return 200 OK even if degraded (DB is OK, optional services down)
	// Kubernetes will not kill the pod if readiness returns 200
	RespondSuccess(c, http.StatusOK, response)
}

// Liveness check endpoint (/live)
func (h *HealthHandler) Liveness(c *gin.Context) {
	RespondSuccess(c, 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) {
	RespondSuccess(c, 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"
	}

	// BE-SVC-016: Récupérer les statistiques détaillées du pool
	poolStats, statsErr := database.GetPoolStats(h.db)
	details := make(map[string]interface{})
	if statsErr == nil {
		details["pool_open_connections"] = poolStats.OpenConnections
		details["pool_in_use"] = poolStats.InUse
		details["pool_idle"] = poolStats.Idle
		details["pool_wait_count"] = poolStats.WaitCount
		details["pool_wait_duration"] = poolStats.WaitDuration.String()
		details["pool_max_idle_closed"] = poolStats.MaxIdleClosed
		details["pool_max_idle_time_closed"] = poolStats.MaxIdleTimeClosed
		details["pool_max_lifetime_closed"] = poolStats.MaxLifetimeClosed
	}

	return HealthCheck{
		Status:    status,
		Message:   "database connection healthy",
		Duration:  float64(duration.Nanoseconds()) / 1e6, // Convert to ms
		Threshold: threshold,
		Details:   details,
	}
}

// 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,
	}
}

// checkS3 vérifie l'état du service S3 (BE-SVC-016)
func (h *HealthHandler) checkS3() HealthCheck {
	start := time.Now()
	threshold := 500.0 // 500ms threshold for S3

	if h.s3Service == nil {
		return HealthCheck{
			Status:  "disabled",
			Message: "S3 storage service not configured",
		}
	}

	// Type assertion pour vérifier si le service a une méthode Health
	// Pour l'instant, on vérifie juste que le service existe
	// Une implémentation complète nécessiterait une interface HealthChecker
	details := make(map[string]interface{})
	details["service"] = "s3_storage"
	details["configured"] = true

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

	return HealthCheck{
		Status:    status,
		Message:   "S3 storage service configured",
		Duration:  float64(duration.Nanoseconds()) / 1e6,
		Threshold: threshold,
		Details:   details,
	}
}

// checkJobWorker vérifie l'état du Job Worker (BE-SVC-016)
func (h *HealthHandler) checkJobWorker() HealthCheck {
	start := time.Now()
	threshold := 50.0 // 50ms threshold

	if h.jobWorker == nil {
		return HealthCheck{
			Status:  "disabled",
			Message: "Job worker not configured",
		}
	}

	// Vérifier que le JobWorker est actif en vérifiant la DB pour les jobs
	// Pour une vérification complète, on pourrait compter les jobs en attente/processing
	details := make(map[string]interface{})
	details["service"] = "job_worker"
	details["configured"] = true

	// Si on a accès à la DB, on peut compter les jobs
	if h.db != nil {
		var pendingCount, processingCount, failedCount int64
		h.db.Table("jobs").Where("status = ?", "pending").Count(&pendingCount)
		h.db.Table("jobs").Where("status = ?", "processing").Count(&processingCount)
		h.db.Table("jobs").Where("status = ?", "failed").Count(&failedCount)

		details["jobs_pending"] = pendingCount
		details["jobs_processing"] = processingCount
		details["jobs_failed"] = failedCount
	}

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

	return HealthCheck{
		Status:    status,
		Message:   "Job worker configured",
		Duration:  float64(duration.Nanoseconds()) / 1e6,
		Threshold: threshold,
		Details:   details,
	}
}

// checkEmailSender vérifie l'état du service d'envoi d'emails (BE-SVC-016)
func (h *HealthHandler) checkEmailSender() HealthCheck {
	start := time.Now()
	threshold := 100.0 // 100ms threshold

	if h.emailSender == nil {
		return HealthCheck{
			Status:  "disabled",
			Message: "Email sender not configured",
		}
	}

	// Vérifier la configuration SMTP
	// Pour une vérification complète, on pourrait tester la connexion SMTP
	details := make(map[string]interface{})
	details["service"] = "email_sender"
	details["configured"] = true

	// Type assertion pour vérifier si c'est un SMTPEmailSender
	// et récupérer la config si possible
	if smtpSender, ok := h.emailSender.(interface {
		GetConfig() interface{}
	}); ok {
		details["smtp_configured"] = true
		_ = smtpSender // Utiliser dans le futur
	} else {
		details["smtp_configured"] = false
	}

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

	return HealthCheck{
		Status:    status,
		Message:   "Email sender configured",
		Duration:  float64(duration.Nanoseconds()) / 1e6,
		Threshold: threshold,
		Details:   details,
	}
}
