package auth

import (
	"context"
	"testing"
	"time"

	"github.com/alicebob/miniredis/v2"
	"github.com/redis/go-redis/v9"
)

func TestSignParseRevoke(t *testing.T) {
	mr := miniredis.RunT(t)
	rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
	j := NewJWT("test-secret", time.Hour, rdb)
	ctx := context.Background()

	token, err := j.Sign("u1")
	if err != nil {
		t.Fatal(err)
	}
	uid, jti, exp, err := j.Parse(ctx, token)
	if err != nil || uid != "u1" || jti == "" {
		t.Fatalf("parse failed: uid=%s jti=%s err=%v", uid, jti, err)
	}
	if exp.IsZero() || time.Until(exp) <= 0 {
		t.Fatalf("expiresAt should be in the future, got %v", exp)
	}

	// 拉黑后解析失败
	if err := j.Revoke(ctx, jti, time.Now().Add(time.Hour)); err != nil {
		t.Fatal(err)
	}
	if _, _, _, err := j.Parse(ctx, token); err == nil {
		t.Fatal("expect parse failure after revoke")
	}

	// 篡改签名失败
	if _, _, _, err := j.Parse(ctx, token+"x"); err == nil {
		t.Fatal("expect parse failure on tampered token")
	}
}

// TestParseFailClosed Redis 不可用时黑名单检查应 fail-closed：拒绝放行（17A）。
func TestParseFailClosed(t *testing.T) {
	mr := miniredis.RunT(t)
	rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
	j := NewJWT("test-secret", time.Hour, rdb)
	ctx := context.Background()

	token, err := j.Sign("u1")
	if err != nil {
		t.Fatal(err)
	}
	// 正常情况下放行
	if _, _, _, err := j.Parse(ctx, token); err != nil {
		t.Fatalf("parse should pass while redis up: %v", err)
	}
	// Redis 故障：Exists 返回 error → 必须拒绝（不得 fail-open）。
	mr.Close()
	if _, _, _, err := j.Parse(ctx, token); err == nil {
		t.Fatal("expect parse failure when redis is unavailable (fail-closed)")
	}
}
