// Package proxy 提供健康检查功能的测试。
//
// 该文件测试健康检查模块的各项功能，包括：
//   - 健康检查器创建
//   - 默认值应用
//   - 自定义配置
//   - 负值配置处理
//   - 零值配置处理
//   - 启动和停止控制
//   - 目标健康检查
//   - 超时处理
//   - 连接失败处理
//   - 标记不健康
//
// 作者：xfy
package proxy

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

	"rua.plus/lolly/internal/config"
	"rua.plus/lolly/internal/loadbalance"
)

// TestNewHealthChecker 测试 NewHealthChecker 函数。
func TestNewHealthChecker(t *testing.T) {
	t.Run("默认值应用", func(t *testing.T) {
		targets := []*loadbalance.Target{
			{URL: "http://backend1:8080"},
		}
		targets[0].Healthy.Store(true)
		cfg := &config.HealthCheckConfig{}

		checker := NewHealthChecker(targets, cfg)

		if checker.GetInterval() != 10*time.Second {
			t.Errorf("Interval = %v, want %v", checker.GetInterval(), 10*time.Second)
		}
		if checker.GetTimeout() != 5*time.Second {
			t.Errorf("Timeout = %v, want %v", checker.GetTimeout(), 5*time.Second)
		}
		if checker.GetPath() != healthPath {
			t.Errorf("Path = %q, want %q", checker.GetPath(), healthPath)
		}
		if checker.IsRunning() {
			t.Error("新建的 checker 应未启动")
		}
	})

	t.Run("自定义配置", func(t *testing.T) {
		targets := []*loadbalance.Target{
			{URL: "http://backend1:8080"},
			{URL: "http://backend2:8080"},
		}
		targets[0].Healthy.Store(true)
		targets[1].Healthy.Store(true)
		cfg := &config.HealthCheckConfig{
			Interval: 30 * time.Second,
			Timeout:  10 * time.Second,
			Path:     "/status",
		}

		checker := NewHealthChecker(targets, cfg)

		if checker.GetInterval() != 30*time.Second {
			t.Errorf("Interval = %v, want %v", checker.GetInterval(), 30*time.Second)
		}
		if checker.GetTimeout() != 10*time.Second {
			t.Errorf("Timeout = %v, want %v", checker.GetTimeout(), 10*time.Second)
		}
		if checker.GetPath() != "/status" {
			t.Errorf("Path = %q, want %q", checker.GetPath(), "/status")
		}
	})

	t.Run("负值配置使用默认值", func(t *testing.T) {
		targets := []*loadbalance.Target{
			{URL: "http://backend1:8080"},
		}
		targets[0].Healthy.Store(true)
		cfg := &config.HealthCheckConfig{
			Interval: -1 * time.Second,
			Timeout:  -1 * time.Second,
		}

		checker := NewHealthChecker(targets, cfg)

		if checker.GetInterval() != 10*time.Second {
			t.Errorf("负值 Interval 应使用默认值，got %v", checker.GetInterval())
		}
		if checker.GetTimeout() != 5*time.Second {
			t.Errorf("负值 Timeout 应使用默认值，got %v", checker.GetTimeout())
		}
	})

	t.Run("零值配置使用默认值", func(t *testing.T) {
		targets := []*loadbalance.Target{
			{URL: "http://backend1:8080"},
		}
		targets[0].Healthy.Store(true)
		cfg := &config.HealthCheckConfig{
			Interval: 0,
			Timeout:  0,
			Path:     "",
		}

		checker := NewHealthChecker(targets, cfg)

		if checker.GetInterval() != 10*time.Second {
			t.Errorf("零值 Interval 应使用默认值，got %v", checker.GetInterval())
		}
		if checker.GetTimeout() != 5*time.Second {
			t.Errorf("零值 Timeout 应使用默认值，got %v", checker.GetTimeout())
		}
		if checker.GetPath() != healthPath {
			t.Errorf("空 Path 应使用默认值，got %q", checker.GetPath())
		}
	})
}

// TestHealthCheckerStartStop 测试 Start 和 Stop 方法。
func TestHealthCheckerStartStop(t *testing.T) {
	t.Run("启动和停止", func(t *testing.T) {
		targets := []*loadbalance.Target{
			{URL: "http://backend1:8080"},
		}
		targets[0].Healthy.Store(true)
		cfg := &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  5 * time.Second,
			Path:     healthPath,
		}

		checker := NewHealthChecker(targets, cfg)

		if checker.IsRunning() {
			t.Error("启动前 IsRunning 应返回 false")
		}

		checker.Start()

		if !checker.IsRunning() {
			t.Error("启动后 IsRunning 应返回 true")
		}

		checker.Stop()

		if checker.IsRunning() {
			t.Error("停止后 IsRunning 应返回 false")
		}
	})

	t.Run("重复启动无效果", func(t *testing.T) {
		targets := []*loadbalance.Target{
			{URL: "http://backend1:8080"},
		}
		targets[0].Healthy.Store(true)
		cfg := &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  5 * time.Second,
		}

		checker := NewHealthChecker(targets, cfg)

		checker.Start()
		checker.Start()

		if !checker.IsRunning() {
			t.Error("重复启动后 checker 应仍在运行")
		}

		checker.Stop()
	})

	t.Run("重复停止无效果", func(t *testing.T) {
		targets := []*loadbalance.Target{
			{URL: "http://backend1:8080"},
		}
		targets[0].Healthy.Store(true)
		cfg := &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  5 * time.Second,
		}

		checker := NewHealthChecker(targets, cfg)

		checker.Stop()
		checker.Stop()

		if checker.IsRunning() {
			t.Error("未启动时停止，checker 应不在运行")
		}
	})
}

// TestCheckTarget 测试 checkTarget 方法。
func TestCheckTarget(t *testing.T) {
	t.Run("健康响应", func(t *testing.T) {
		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.URL.Path != healthPath {
				t.Errorf("请求路径 = %q, want %q", r.URL.Path, healthPath)
			}
			w.WriteHeader(http.StatusOK)
		}))
		defer server.Close()

		target := &loadbalance.Target{
			URL: server.URL,
		}
		target.Healthy.Store(false)

		checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  5 * time.Second,
			Path:     healthPath,
		})

		checker.checkTarget(target)

		if !target.Healthy.Load() {
			t.Error("健康响应后 target 应标记为 healthy")
		}
	})

	t.Run("不健康响应", func(t *testing.T) {
		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			w.WriteHeader(http.StatusInternalServerError)
		}))
		defer server.Close()

		target := &loadbalance.Target{
			URL: server.URL,
		}
		target.Healthy.Store(true)

		checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  5 * time.Second,
			Path:     healthPath,
		})

		checker.checkTarget(target)

		if target.Healthy.Load() {
			t.Error("5xx 响应后 target 应标记为 unhealthy")
		}
	})

	t.Run("超时", func(t *testing.T) {
		server := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
			time.Sleep(100 * time.Millisecond)
		}))
		defer server.Close()

		target := &loadbalance.Target{
			URL: server.URL,
		}
		target.Healthy.Store(true)

		checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  10 * time.Millisecond,
			Path:     healthPath,
		})

		checker.checkTarget(target)

		if target.Healthy.Load() {
			t.Error("超时后 target 应标记为 unhealthy")
		}
	})

	t.Run("连接失败", func(t *testing.T) {
		target := &loadbalance.Target{
			URL: "http://invalid-host-that-does-not-exist:99999",
		}
		target.Healthy.Store(true)

		checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  100 * time.Millisecond,
			Path:     healthPath,
		})

		checker.checkTarget(target)

		if target.Healthy.Load() {
			t.Error("连接失败后 target 应标记为 unhealthy")
		}
	})

	t.Run("3xx 重定向响应", func(t *testing.T) {
		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			w.WriteHeader(http.StatusMovedPermanently)
		}))
		defer server.Close()

		target := &loadbalance.Target{
			URL: server.URL,
		}
		target.Healthy.Store(true)

		checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  5 * time.Second,
			Path:     healthPath,
		})

		checker.checkTarget(target)

		if target.Healthy.Load() {
			t.Error("3xx 响应后 target 应标记为 unhealthy")
		}
	})

	t.Run("4xx 客户端错误响应", func(t *testing.T) {
		server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			w.WriteHeader(http.StatusNotFound)
		}))
		defer server.Close()

		target := &loadbalance.Target{
			URL: server.URL,
		}
		target.Healthy.Store(true)

		checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  5 * time.Second,
			Path:     healthPath,
		})

		checker.checkTarget(target)

		if target.Healthy.Load() {
			t.Error("4xx 响应后 target 应标记为 unhealthy")
		}
	})

	t.Run("2xx 成功响应", func(t *testing.T) {
		tests := []struct {
			name       string
			statusCode int
		}{
			{"200 OK", http.StatusOK},
			{"201 Created", http.StatusCreated},
			{"204 No Content", http.StatusNoContent},
		}

		for _, tt := range tests {
			t.Run(tt.name, func(t *testing.T) {
				server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
					w.WriteHeader(tt.statusCode)
				}))
				defer server.Close()

				target := &loadbalance.Target{
					URL: server.URL,
				}
				target.Healthy.Store(false)

				checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
					Interval: 1 * time.Hour,
					Timeout:  5 * time.Second,
					Path:     healthPath,
				})

				checker.checkTarget(target)

				if !target.Healthy.Load() {
					t.Errorf("%d 响应后 target 应标记为 healthy", tt.statusCode)
				}
			})
		}
	})
}

// TestMarkUnhealthy 测试 MarkUnhealthy 方法。
func TestMarkUnhealthy(t *testing.T) {
	t.Run("标记不健康", func(t *testing.T) {
		target := &loadbalance.Target{
			URL: "http://backend1:8080",
		}
		target.Healthy.Store(true)

		checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  5 * time.Second,
			Path:     healthPath,
		})

		checker.MarkUnhealthy(target)

		if target.Healthy.Load() {
			t.Error("MarkUnhealthy 后 target 应标记为 unhealthy")
		}
	})

	t.Run("已不健康的 target 再次标记", func(t *testing.T) {
		target := &loadbalance.Target{
			URL: "http://backend1:8080",
		}
		target.Healthy.Store(false)

		checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  5 * time.Second,
			Path:     healthPath,
		})

		checker.MarkUnhealthy(target)

		if target.Healthy.Load() {
			t.Error("MarkUnhealthy 后 target 应保持 unhealthy 状态")
		}
	})

	t.Run("多 target 场景", func(t *testing.T) {
		target1 := &loadbalance.Target{
			URL: "http://backend1:8080",
		}
		target1.Healthy.Store(true)
		target2 := &loadbalance.Target{
			URL: "http://backend2:8080",
		}
		target2.Healthy.Store(true)

		checker := NewHealthChecker([]*loadbalance.Target{target1, target2}, &config.HealthCheckConfig{
			Interval: 1 * time.Hour,
			Timeout:  5 * time.Second,
			Path:     healthPath,
		})

		checker.MarkUnhealthy(target1)

		if target1.Healthy.Load() {
			t.Error("target1 应标记为 unhealthy")
		}
		if !target2.Healthy.Load() {
			t.Error("target2 应保持 healthy")
		}
	})
}

// TestMarkUnhealthy_WithSlowStartManager 测试 MarkUnhealthy 与 SlowStartManager 集成。
func TestMarkUnhealthy_WithSlowStartManager(t *testing.T) {
	target := &loadbalance.Target{
		URL:    "http://127.0.0.1:8080",
		Weight: 100,
	}
	target.Healthy.Store(true)
	target.SlowStart = 30 * time.Second

	checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
		Interval:  1 * time.Hour,
		Timeout:   5 * time.Second,
		Path:      "/health",
		SlowStart: 30 * time.Second,
	})

	// 先标记为健康以初始化慢启动
	checker.MarkHealthy(target)

	// 标记目标为不健康
	checker.MarkUnhealthy(target)

	if target.Healthy.Load() {
		t.Error("target 应标记为 unhealthy")
	}
}

// TestMarkHealthy_WithSlowStartManager 测试 MarkHealthy 与 SlowStartManager 集成。
func TestMarkHealthy_WithSlowStartManager(t *testing.T) {
	target := &loadbalance.Target{
		URL:    "http://127.0.0.1:8080",
		Weight: 100,
	}
	target.Healthy.Store(false)
	target.SlowStart = 30 * time.Second

	checker := NewHealthChecker([]*loadbalance.Target{target}, &config.HealthCheckConfig{
		Interval:  1 * time.Hour,
		Timeout:   5 * time.Second,
		Path:      "/health",
		SlowStart: 30 * time.Second,
	})

	// 标记目标为健康
	checker.MarkHealthy(target)

	if !target.Healthy.Load() {
		t.Error("target 应标记为 healthy")
	}

	// 验证慢启动已开始（EffectiveWeight 应被设置为 1）
	ew := target.EffectiveWeight.Load()
	if ew <= 0 {
		t.Errorf("慢启动 EffectiveWeight 应大于 0，got: %d", ew)
	}
}
