package handlers

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

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

func setupTestHealthRouter() (*gin.Engine, *HealthHandler) {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	logger := zap.NewNop()
	db, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	handler := NewHealthHandler(db, logger, nil, nil, "test")

	router.GET("/health", handler.Check)
	router.GET("/health/detailed", handler.Health)
	router.GET("/ready", handler.Readiness)
	router.GET("/live", handler.Liveness)
	router.GET("/health/simple", SimpleHealthCheck)

	return router, handler
}

func TestHealthHandler_Check_Success(t *testing.T) {
	// Setup
	router, _ := setupTestHealthRouter()

	// Execute
	req, _ := http.NewRequest("GET", "/health", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusOK, w.Code)

	var apiResponse APIResponse
	err := json.Unmarshal(w.Body.Bytes(), &apiResponse)
	assert.NoError(t, err)
	assert.True(t, apiResponse.Success)

	var data map[string]interface{}
	dataBytes, _ := json.Marshal(apiResponse.Data)
	json.Unmarshal(dataBytes, &data)
	assert.Equal(t, "ok", data["status"])
}

func TestHealthHandler_Health_Success(t *testing.T) {
	// Setup
	router, _ := setupTestHealthRouter()

	// Execute
	req, _ := http.NewRequest("GET", "/health/detailed", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	// May return 503 if services are degraded, but should still have response structure
	assert.True(t, w.Code == http.StatusOK || w.Code == http.StatusServiceUnavailable)

	var apiResponse APIResponse
	err := json.Unmarshal(w.Body.Bytes(), &apiResponse)
	assert.NoError(t, err)

	var response HealthResponse
	responseBytes, _ := json.Marshal(apiResponse.Data)
	json.Unmarshal(responseBytes, &response)
	assert.Contains(t, response.Checks, "database")
	assert.Contains(t, response.Checks, "redis")
	assert.Contains(t, response.Checks, "rabbitmq")
}

func TestHealthHandler_Readiness_Success(t *testing.T) {
	// Setup
	router, _ := setupTestHealthRouter()

	// Execute
	req, _ := http.NewRequest("GET", "/ready", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusOK, w.Code)

	var apiResponse APIResponse
	err := json.Unmarshal(w.Body.Bytes(), &apiResponse)
	assert.NoError(t, err)

	var response HealthResponse
	responseBytes, _ := json.Marshal(apiResponse.Data)
	json.Unmarshal(responseBytes, &response)
	assert.Contains(t, []string{"ready", "degraded"}, response.Status)
	assert.Contains(t, response.Checks, "database")
}

func TestHealthHandler_Liveness_Success(t *testing.T) {
	// Setup
	router, _ := setupTestHealthRouter()

	// Execute
	req, _ := http.NewRequest("GET", "/live", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusOK, w.Code)

	var apiResponse APIResponse
	err := json.Unmarshal(w.Body.Bytes(), &apiResponse)
	assert.NoError(t, err)

	var response map[string]interface{}
	responseBytes, _ := json.Marshal(apiResponse.Data)
	json.Unmarshal(responseBytes, &response)
	assert.Equal(t, "alive", response["status"])
	assert.Contains(t, response, "timestamp")
}

func TestSimpleHealthCheck_Success(t *testing.T) {
	// Setup
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.GET("/health/simple", SimpleHealthCheck)

	// Execute
	req, _ := http.NewRequest("GET", "/health/simple", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusOK, w.Code)

	var apiResponse APIResponse
	err := json.Unmarshal(w.Body.Bytes(), &apiResponse)
	assert.NoError(t, err)

	var response map[string]interface{}
	responseBytes, _ := json.Marshal(apiResponse.Data)
	json.Unmarshal(responseBytes, &response)
	assert.Equal(t, "healthy", response["status"])
	assert.Equal(t, "veza-backend-api", response["service"])
}

func TestHealthHandler_Health_WithOptionalServices(t *testing.T) {
	// Setup
	gin.SetMode(gin.TestMode)
	router := gin.New()

	logger := zap.NewNop()
	db, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	handler := NewHealthHandlerWithServices(db, logger, nil, nil, "test", "s3Service", "jobWorker", "emailSender")

	router.GET("/health/detailed", handler.Health)

	// Execute
	req, _ := http.NewRequest("GET", "/health/detailed", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.True(t, w.Code == http.StatusOK || w.Code == http.StatusServiceUnavailable)

	var apiResponse APIResponse
	err := json.Unmarshal(w.Body.Bytes(), &apiResponse)
	assert.NoError(t, err)

	var response HealthResponse
	responseBytes, _ := json.Marshal(apiResponse.Data)
	json.Unmarshal(responseBytes, &response)
	assert.Contains(t, response.Checks, "s3_storage")
	assert.Contains(t, response.Checks, "job_worker")
	assert.Contains(t, response.Checks, "email_sender")
}

func TestHealthHandler_NewHealthHandlerSimple(t *testing.T) {
	// Setup
	db, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	handler := NewHealthHandlerSimple(db)

	// Assert
	assert.NotNil(t, handler)
	assert.NotNil(t, handler.db)
}
