package grpchealth

import (
	"context"
	"time"

	"google.golang.org/grpc/codes"
	healthgrpc "google.golang.org/grpc/health/grpc_health_v1"
	healthpb "google.golang.org/grpc/health/grpc_health_v1"
	"google.golang.org/grpc/status"
)

const (
	// StatusOK 表示单个健康检查项通过。
	StatusOK = "ok"
	// StatusFail 表示单个健康检查项失败。
	StatusFail = "fail"
)

// Check 描述一项 readiness 子检查；gRPC health 只返回总状态，子项用于服务内聚合。
type Check struct {
	Name    string `json:"name"`
	Status  string `json:"status"`
	Message string `json:"message,omitempty"`
}

// Checker 把服务自己的 ready 语义适配到标准 gRPC health protocol。
type Checker interface {
	ServiceName() string
	Ready(ctx context.Context) []Check
}

// Server 实现 grpc.health.v1.Health，并把 SERVING 严格映射到 Checker.Ready。
type Server struct {
	healthgrpc.UnimplementedHealthServer

	checker Checker
}

// NewServer 创建标准 gRPC health server。
func NewServer(checker Checker) *Server {
	return &Server{checker: checker}
}

// Check 返回当前服务是否 ready；未知服务名按标准返回 NotFound。
func (s *Server) Check(ctx context.Context, req *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) {
	if !s.supports(req.GetService()) {
		return nil, status.Error(codes.NotFound, "unknown service")
	}

	return &healthpb.HealthCheckResponse{Status: s.status(ctx)}, nil
}

// Watch 定时推送 ready 状态变化，供支持 gRPC health watch 的服务发现系统消费。
func (s *Server) Watch(req *healthpb.HealthCheckRequest, stream healthgrpc.Health_WatchServer) error {
	if !s.supports(req.GetService()) {
		if err := stream.Send(&healthpb.HealthCheckResponse{Status: healthpb.HealthCheckResponse_SERVICE_UNKNOWN}); err != nil {
			return status.Error(codes.Canceled, "health watch stream ended")
		}
		<-stream.Context().Done()
		return status.Error(codes.Canceled, "health watch stream ended")
	}

	ticker := time.NewTicker(2 * time.Second)
	defer ticker.Stop()

	var last healthpb.HealthCheckResponse_ServingStatus = -1
	for {
		current := s.status(stream.Context())
		if current != last {
			if err := stream.Send(&healthpb.HealthCheckResponse{Status: current}); err != nil {
				return status.Error(codes.Canceled, "health watch stream ended")
			}
			last = current
		}

		select {
		case <-ticker.C:
		case <-stream.Context().Done():
			return status.Error(codes.Canceled, "health watch stream ended")
		}
	}
}

// Healthy 聚合子检查，任何失败项都会让服务从 ready 池摘除。
func Healthy(checks []Check) bool {
	for _, check := range checks {
		if check.Status != StatusOK {
			return false
		}
	}

	return true
}

// OK 创建通过项。
func OK(name string) Check {
	return Check{Name: name, Status: StatusOK}
}

// Fail 创建失败项。
func Fail(name string, message string) Check {
	return Check{Name: name, Status: StatusFail, Message: message}
}

// Bool 根据布尔条件创建检查项。
func Bool(name string, ok bool, message string) Check {
	if ok {
		return OK(name)
	}

	return Fail(name, message)
}

func (s *Server) supports(service string) bool {
	return service == "" || service == s.checker.ServiceName()
}

func (s *Server) status(ctx context.Context) healthpb.HealthCheckResponse_ServingStatus {
	if Healthy(s.checker.Ready(ctx)) {
		return healthpb.HealthCheckResponse_SERVING
	}

	return healthpb.HealthCheckResponse_NOT_SERVING
}
