//go:build !integration
// +build !integration

package handlers

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

// setupTestHealthHandler crée un handler de test avec une DB en mémoire
func setupTestHealthHandler(t *testing.T) (*HealthHandler, *gorm.DB, func()) {
	// DB en mémoire SQLite
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)

	logger := zap.NewNop()
	handler := NewHealthHandler(db, logger, nil, nil, "test")

	cleanup := func() {
		sqlDB, _ := db.DB()
		if sqlDB != nil {
			sqlDB.Close()
		}
	}

	return handler, db, cleanup
}

// TestReadiness_DBOK_OptionalServicesDown_Returns200Degraded vérifie que /readyz retourne 200 avec status "degraded" si DB OK mais optionnels KO
func TestReadiness_DBOK_OptionalServicesDown_Returns200Degraded(t *testing.T) {
	gin.SetMode(gin.TestMode)
	handler, _, cleanup := setupTestHealthHandler(t)
	defer cleanup()

	// Créer un handler avec Redis/RabbitMQ non configurés (simule services down)
	// Le handler est déjà configuré avec redis=nil et rabbitMQ=nil dans setupTestHealthHandler

	req := httptest.NewRequest("GET", "/readyz", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req

	// Exécuter
	handler.Readiness(c)

	// Vérifier : doit retourner 200 OK avec status "degraded"
	assert.Equal(t, http.StatusOK, w.Code)

	// Parser la réponse JSON
	var response struct {
		Success bool                   `json:"success"`
		Data    map[string]interface{} `json:"data"`
	}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response.Success)

	// Vérifier que le status est "degraded" ou "ready" selon la configuration
	// Si Redis/RabbitMQ ne sont pas configurés, ils retournent "error"
	// mais le status global doit être "degraded" si DB est OK
	status := response.Data["status"].(string)
	assert.Contains(t, []string{"ready", "degraded"}, status)

	// Vérifier que la DB est OK
	checks := response.Data["checks"].(map[string]interface{})
	dbCheck := checks["database"].(map[string]interface{})
	assert.Equal(t, "ok", dbCheck["status"].(string))
}

// TestReadiness_DBDown_Returns503 vérifie que /readyz retourne 503 si DB est down
func TestReadiness_DBDown_Returns503(t *testing.T) {
	gin.SetMode(gin.TestMode)

	// Créer un handler avec une DB nil (simule DB down)
	logger := zap.NewNop()
	handler := NewHealthHandler(nil, logger, nil, nil, "test")

	req := httptest.NewRequest("GET", "/readyz", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req

	// Exécuter
	handler.Readiness(c)

	// Vérifier : doit retourner 503 Service Unavailable
	assert.Equal(t, http.StatusServiceUnavailable, w.Code)
}

// TestReadiness_AllServicesOK_Returns200Ready vérifie que /readyz retourne 200 avec status "ready" si tous les services sont OK
// Note: Dans ce test, Redis/RabbitMQ ne sont pas configurés, donc le status sera "degraded" (comportement attendu)
func TestReadiness_AllServicesOK_Returns200Ready(t *testing.T) {
	gin.SetMode(gin.TestMode)
	handler, _, cleanup := setupTestHealthHandler(t)
	defer cleanup()

	req := httptest.NewRequest("GET", "/readyz", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req

	// Exécuter
	handler.Readiness(c)

	// Vérifier : doit retourner 200 OK (même si degraded)
	assert.Equal(t, http.StatusOK, w.Code)

	// Parser la réponse JSON
	var response struct {
		Success bool                   `json:"success"`
		Data    map[string]interface{} `json:"data"`
	}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response.Success)

	// Si Redis/RabbitMQ ne sont pas configurés, le status sera "degraded" (comportement attendu)
	// Si tous les services sont configurés et OK, le status sera "ready"
	status := response.Data["status"].(string)
	assert.Contains(t, []string{"ready", "degraded"}, status)

	// Dans tous les cas, la DB doit être OK
	checks := response.Data["checks"].(map[string]interface{})
	dbCheck := checks["database"].(map[string]interface{})
	assert.Equal(t, "ok", dbCheck["status"].(string))
}
