package routes

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

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
)

func TestRegisterCommonRoutes(t *testing.T) {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	RegisterCommonRoutes(r)

	// Test /health
	w := httptest.NewRecorder()
	req := httptest.NewRequest(http.MethodGet, "/health", nil)
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)

	// Test /live
	w = httptest.NewRecorder()
	req = httptest.NewRequest(http.MethodGet, "/live", nil)
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)

	// Test /ready (no health checker set)
	w = httptest.NewRecorder()
	req = httptest.NewRequest(http.MethodGet, "/ready", nil)
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)

	// Test /metrics
	w = httptest.NewRecorder()
	req = httptest.NewRequest(http.MethodGet, "/metrics", nil)
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)

	// Test /setup/status
	w = httptest.NewRecorder()
	req = httptest.NewRequest(http.MethodGet, "/setup/status", nil)
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)

	var setupResp map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &setupResp)
	assert.NoError(t, err)
	assert.Equal(t, float64(0), setupResp["code"])

	// Test POST /api/event_logging/batch
	w = httptest.NewRecorder()
	req = httptest.NewRequest(http.MethodPost, "/api/event_logging/batch", nil)
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
}

func TestHealthHandler_NoHealthChecker(t *testing.T) {
	gin.SetMode(gin.TestMode)

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest(http.MethodGet, "/health", nil)

	healthHandler(c)

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

	var resp map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &resp)
	assert.NoError(t, err)
	assert.Equal(t, "ok", resp["status"])

	components, ok := resp["components"].(map[string]interface{})
	assert.True(t, ok)
	db, ok := components["database"].(map[string]interface{})
	assert.True(t, ok)
	assert.Equal(t, "unknown", db["status"])
}

func TestReadinessHandler_NoHealthChecker(t *testing.T) {
	gin.SetMode(gin.TestMode)

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest(http.MethodGet, "/ready", nil)

	readinessHandler(c)

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

	var resp map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &resp)
	assert.NoError(t, err)
	assert.Equal(t, "ready", resp["status"])
}

func TestLivenessHandler(t *testing.T) {
	gin.SetMode(gin.TestMode)

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest(http.MethodGet, "/live", nil)

	livenessHandler(c)

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

	var resp map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &resp)
	assert.NoError(t, err)
	assert.Equal(t, "alive", resp["status"])

	// Verify timestamp is valid
	ts, ok := resp["timestamp"].(string)
	assert.True(t, ok)
	_, err = time.Parse(time.RFC3339, ts)
	assert.NoError(t, err)
}

// mockHealthChecker implements HealthChecker for testing
type mockHealthChecker struct {
	dbHealthy    bool
	redisHealthy bool
}

func (m *mockHealthChecker) CheckDatabase() bool { return m.dbHealthy }
func (m *mockHealthChecker) CheckRedis() bool     { return m.redisHealthy }

func TestHealthHandler_WithHealthyChecker(t *testing.T) {
	gin.SetMode(gin.TestMode)

	// Reset the healthChecker for test
	healthChecker = &mockHealthChecker{dbHealthy: true, redisHealthy: true}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest(http.MethodGet, "/health", nil)

	healthHandler(c)

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

	var resp map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &resp)
	assert.NoError(t, err)
	assert.Equal(t, "ok", resp["status"])
}

func TestHealthHandler_WithUnhealthyDB(t *testing.T) {
	gin.SetMode(gin.TestMode)

	healthChecker = &mockHealthChecker{dbHealthy: false, redisHealthy: true}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest(http.MethodGet, "/health", nil)

	healthHandler(c)

	assert.Equal(t, http.StatusServiceUnavailable, w.Code)

	var resp map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &resp)
	assert.NoError(t, err)
	assert.Equal(t, "degraded", resp["status"])
}

func TestHealthHandler_WithUnhealthyRedis(t *testing.T) {
	gin.SetMode(gin.TestMode)

	healthChecker = &mockHealthChecker{dbHealthy: true, redisHealthy: false}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest(http.MethodGet, "/health", nil)

	healthHandler(c)

	assert.Equal(t, http.StatusServiceUnavailable, w.Code)

	var resp map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &resp)
	assert.NoError(t, err)
	assert.Equal(t, "degraded", resp["status"])
}

func TestReadinessHandler_AllHealthy(t *testing.T) {
	gin.SetMode(gin.TestMode)

	healthChecker = &mockHealthChecker{dbHealthy: true, redisHealthy: true}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest(http.MethodGet, "/ready", nil)

	readinessHandler(c)

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

	var resp map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &resp)
	assert.NoError(t, err)
	assert.Equal(t, "ready", resp["status"])
}

func TestReadinessHandler_NotReady(t *testing.T) {
	gin.SetMode(gin.TestMode)

	healthChecker = &mockHealthChecker{dbHealthy: true, redisHealthy: false}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest(http.MethodGet, "/ready", nil)

	readinessHandler(c)

	assert.Equal(t, http.StatusServiceUnavailable, w.Code)

	var resp map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &resp)
	assert.NoError(t, err)
	assert.Equal(t, "not_ready", resp["status"])
}

func TestDelegatedMetricFunctions(t *testing.T) {
	// These should not panic - they delegate to prommetrics package
	RecordHTTPRequest("GET", "/test", 200, 100*time.Millisecond)
	SetDBConnections(5, 3)
	SetRedisConnections(10, 4)
	SetActiveAccounts(50)
	SetRequestQueueDepth(5)
	SetOpsHealthScore(90)
	SetErrorRate(0.1)
	SetSuccessRate(0.9)
	SetQPS(100.0)
	SetTPS(500.0)
}
