package health

import (
	"log/slog"
	"net/http"
	"sync"
	"time"

	"github.com/edu-cloud/api-gateway/internal/config"
	"github.com/gin-gonic/gin"
)

// Healthz 存活探针（liveness）。
// GET /healthz：返回 200 {"status":"ok"} 表示进程存活。
func Healthz(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status": "ok",
	})
}

// downstreamCheck 描述一个下游服务的健康检查配置。
type downstreamCheck struct {
	name     string // 服务名（用于响应体与日志）
	url      string // /healthz 完整 URL
	required bool   // true=必需依赖（失败返回 503），false=可选依赖（失败返回 200+degraded）
}

// Readyz 就绪探针（readiness，W4 裁决）。
// GET /readyz：并行 ping 下游服务 /healthz，超时 2s。
//
// 软失败规则（president-final-rulings.md §3.3）：
//   - 必需依赖（iam / teacher-bff，P2 已就绪）失败 → 503
//   - 可选依赖（P3-P5 未就绪服务）失败 → 200 + degraded 列表
//   - 全部可达 → 200 {"status":"ok"}
func Readyz(cfg *config.Config) gin.HandlerFunc {
	checks := []downstreamCheck{
		{name: "iam", url: cfg.IamServiceURL + "/healthz", required: true},
		{name: "teacher-bff", url: cfg.TeacherBffURL + "/healthz", required: true},
		{name: "core-edu", url: cfg.CoreEduServiceURL + "/healthz", required: false},
		{name: "content", url: cfg.ContentServiceURL + "/healthz", required: false},
		{name: "msg", url: cfg.MsgServiceURL + "/healthz", required: false},
		{name: "ai", url: cfg.AiServiceURL + "/healthz", required: false},
		{name: "data-ana", url: cfg.DataAnaServiceURL + "/healthz", required: false},
		{name: "student-bff", url: cfg.StudentBffURL + "/healthz", required: false},
		{name: "parent-bff", url: cfg.ParentBffURL + "/healthz", required: false},
	}

	client := &http.Client{Timeout: 2 * time.Second}

	return func(c *gin.Context) {
		var (
			mu        sync.Mutex
			unhealthy []string
			degraded  []string
		)

		var wg sync.WaitGroup
		for _, check := range checks {
			wg.Add(1)
			go func(dc downstreamCheck) {
				defer wg.Done()
				resp, err := client.Get(dc.url)
				if err != nil {
					mu.Lock()
					if dc.required {
						unhealthy = append(unhealthy, dc.name)
					} else {
						degraded = append(degraded, dc.name)
					}
					mu.Unlock()
					slog.Warn("downstream health check failed",
						"service", dc.name,
						"required", dc.required,
						"error", err,
					)
					return
				}
				resp.Body.Close()
				if resp.StatusCode != http.StatusOK {
					mu.Lock()
					if dc.required {
						unhealthy = append(unhealthy, dc.name)
					} else {
						degraded = append(degraded, dc.name)
					}
					mu.Unlock()
					slog.Warn("downstream unhealthy",
						"service", dc.name,
						"required", dc.required,
						"status", resp.StatusCode,
					)
				}
			}(check)
		}
		wg.Wait()

		if len(unhealthy) > 0 {
			c.JSON(http.StatusServiceUnavailable, gin.H{
				"status":    "error",
				"unhealthy": unhealthy,
				"degraded":  degraded,
			})
			return
		}

		resp := gin.H{"status": "ok"}
		if len(degraded) > 0 {
			resp["degraded"] = degraded
		}
		c.JSON(http.StatusOK, resp)
	}
}
