// Package healthcheck 验证 room-service ready/live 对存储、lease 和 drain 状态的映射。
package healthcheck

import (
	"context"
	"errors"
	"testing"
	"time"

	"hyapp/pkg/grpchealth"
)

// okRuntime 模拟领域服务进程内状态正常。
type okRuntime struct{}

// HealthCheck 返回 nil，让测试聚焦外部依赖和 listener 状态。
func (okRuntime) HealthCheck() error {
	return nil
}

// fakeMySQL 模拟 MySQL ping 结果。
type fakeMySQL struct {
	// err 是 Ping 返回的错误。
	err error
}

// Ping 实现 MySQLPinger。
func (m fakeMySQL) Ping(context.Context) error {
	return m.err
}

// fakeRedis 分别模拟 Redis ping 和 lease Lua 探针结果。
type fakeRedis struct {
	pingErr  error
	leaseErr error
}

// Ping 实现 RedisProbe 的基础连通性检查。
func (r fakeRedis) Ping(context.Context) error {
	return r.pingErr
}

// ProbeLease 实现 RedisProbe 的 lease 原子路径检查。
func (r fakeRedis) ProbeLease(context.Context, string, time.Time, time.Duration) error {
	return r.leaseErr
}

// TestReadyRequiresStorageAndLease 验证 ready 必须同时满足 listener、runtime、MySQL 和 Redis。
func TestReadyRequiresStorageAndLease(t *testing.T) {
	state := NewState("room-node-test", okRuntime{}, fakeMySQL{}, fakeRedis{})
	// app.Run 进入 Serve 后会设置该标记；测试直接设置以隔离 State 行为。
	state.MarkGRPCServing()

	if !grpchealth.Healthy(state.Ready(context.Background())) {
		t.Fatalf("ready should pass when listener, runtime, MySQL and Redis probes pass: %+v", state.Ready(context.Background()))
	}

	state.MarkDraining()
	// drain 中的节点不能继续接管房间或接收新命令。
	if grpchealth.Healthy(state.Ready(context.Background())) {
		t.Fatalf("ready should fail while draining")
	}
}

// TestReadyFailsWhenRedisLeaseProbeFails 验证 Redis 连通但 Lua lease 路径失败时 ready 必须失败。
func TestReadyFailsWhenRedisLeaseProbeFails(t *testing.T) {
	state := NewState("room-node-test", okRuntime{}, fakeMySQL{}, fakeRedis{leaseErr: errors.New("lease eval failed")})
	state.MarkGRPCServing()

	checks := state.Ready(context.Background())
	if grpchealth.Healthy(checks) {
		t.Fatalf("ready should fail when Redis lease probe fails")
	}

	found := false
	for _, check := range checks {
		if check.Name == "redis_lease" && check.Status == grpchealth.StatusFail {
			// redis_lease 单独暴露，便于区分 Redis ping 成功但 owner 仲裁不可用。
			found = true
		}
	}
	if !found {
		t.Fatalf("ready failure should include redis_lease check: %+v", checks)
	}
}
