package yuhticket

import (
	"testing"
	"time"
)

func TestSignVerifyRoundTrip(t *testing.T) {
	secret := "test-secret"
	now := time.Unix(1_700_000_000, 0).UTC()
	c := Claims{
		Iss: "yuheng", Aud: "aijianzhan", Phone: "13531041944",
		HostKey: "host-1", Exp: now.Add(90 * time.Second).Unix(), JTI: "jti-ok", Scope: "sync_bind",
	}
	tok, err := Sign(secret, c)
	if err != nil {
		t.Fatal(err)
	}
	got, err := Verify(tok, VerifyOpts{Secret: secret, Now: now})
	if err != nil {
		t.Fatal(err)
	}
	if got.Phone != c.Phone || got.HostKey != c.HostKey {
		t.Fatalf("claims mismatch: %+v", got)
	}
}

func TestVerifyBadSig(t *testing.T) {
	now := time.Unix(1_700_000_000, 0).UTC()
	tok, _ := Sign("a", Claims{
		Phone: "13800000001", HostKey: "h", Exp: now.Add(60 * time.Second).Unix(), JTI: "j1",
	})
	if _, err := Verify(tok, VerifyOpts{Secret: "b", Now: now}); err == nil {
		t.Fatal("expected bad sig")
	}
}

func TestSignVerifyRestoreNoPhone(t *testing.T) {
	secret := "test-secret"
	now := time.Unix(1_700_000_000, 0).UTC()
	c := Claims{
		Iss: "yuheng", Aud: "aijianzhan", HostKey: "6655aabbccddeeff00112233",
		Exp: now.Add(90 * time.Second).Unix(), JTI: "jti-restore", Scope: "sync_restore",
		YuhengUserID: "6655aabbccddeeff00112233",
	}
	tok, err := Sign(secret, c)
	if err != nil {
		t.Fatal(err)
	}
	got, err := Verify(tok, VerifyOpts{
		Secret: secret, Now: now, AllowEmptyPhone: true,
		AllowedScopes: []string{"sync_bind", "sync_restore"},
	})
	if err != nil {
		t.Fatal(err)
	}
	if got.HostKey != c.HostKey || got.Phone != "" {
		t.Fatalf("claims mismatch: %+v", got)
	}
	// 默认 Verify（须 phone）应拒绝无 phone 的 sync_bind；sync_restore 在默认 scopes 外也应拒绝
	if _, err := Verify(tok, VerifyOpts{Secret: secret, Now: now}); err == nil {
		t.Fatal("expected scope reject without AllowedScopes")
	}
}

func TestSignRequiresPhoneForSyncBind(t *testing.T) {
	_, err := Sign("s", Claims{
		HostKey: "h", Exp: time.Now().Add(time.Minute).Unix(), JTI: "j", Scope: "sync_bind",
	})
	if err == nil {
		t.Fatal("expected phone required for sync_bind")
	}
}

func TestJTIConsume(t *testing.T) {
	dir := t.TempDir()
	st, err := NewJTIStore(dir)
	if err != nil {
		t.Fatal(err)
	}
	exp := time.Now().Add(time.Minute).Unix()
	if err := st.Consume("x", exp); err != nil {
		t.Fatal(err)
	}
	if err := st.Consume("x", exp); err == nil {
		t.Fatal("expected replay")
	}
}
