package store

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

// newUserRepo 返回一个可测试的 UserRepository 实现。
// 如需测试 Pg 实现，可在此替换为连接真实 DB 的版本。
func newUserRepo() UserRepository {
	return NewMemUserRepository()
}

func TestUserRepository_Create(t *testing.T) {
	repo := newUserRepo()
	ctx := context.Background()

	id, err := repo.Create(ctx, "alice", "hash123")
	if err != nil {
		t.Fatalf("Create failed: %v", err)
	}
	if id == "" {
		t.Fatal("expected non-empty ID")
	}

	// 重复用户名应返回 ErrUsernameTaken
	_, err = repo.Create(ctx, "alice", "hash456")
	if !errors.Is(err, ErrUsernameTaken) {
		t.Fatalf("expected ErrUsernameTaken, got %v", err)
	}
}

func TestUserRepository_FindByUsername(t *testing.T) {
	repo := newUserRepo()
	ctx := context.Background()

	_, err := repo.Create(ctx, "bob", "hash_bob")
	if err != nil {
		t.Fatalf("Create failed: %v", err)
	}

	user, err := repo.FindByUsername(ctx, "bob")
	if err != nil {
		t.Fatalf("FindByUsername failed: %v", err)
	}
	if user.Username != "bob" {
		t.Fatalf("expected username bob, got %s", user.Username)
	}
	if user.PasswordHash != "hash_bob" {
		t.Fatalf("expected password hash hash_bob, got %s", user.PasswordHash)
	}

	// 不存在的用户
	_, err = repo.FindByUsername(ctx, "nobody")
	if !errors.Is(err, ErrUserNotFound) {
		t.Fatalf("expected ErrUserNotFound, got %v", err)
	}
}

func TestUserRepository_FindByID(t *testing.T) {
	repo := newUserRepo()
	ctx := context.Background()

	id, err := repo.Create(ctx, "charlie", "hash_charlie")
	if err != nil {
		t.Fatalf("Create failed: %v", err)
	}

	user, err := repo.FindByID(ctx, id)
	if err != nil {
		t.Fatalf("FindByID failed: %v", err)
	}
	if user.ID != id {
		t.Fatalf("expected ID %s, got %s", id, user.ID)
	}
	if user.Username != "charlie" {
		t.Fatalf("expected username charlie, got %s", user.Username)
	}

	// 不存在的 ID
	_, err = repo.FindByID(ctx, "nonexistent-uuid")
	if !errors.Is(err, ErrUserNotFound) {
		t.Fatalf("expected ErrUserNotFound, got %v", err)
	}
}

func TestUserRepository_RefreshToken(t *testing.T) {
	repo := newUserRepo()
	ctx := context.Background()

	userID, err := repo.Create(ctx, "dave", "hash_dave")
	if err != nil {
		t.Fatalf("Create failed: %v", err)
	}

	tokenHash := "abc123hash"
	expiresAt := time.Now().Add(7 * 24 * time.Hour)

	// 保存 token
	if err := repo.SaveRefreshToken(ctx, userID, tokenHash, expiresAt); err != nil {
		t.Fatalf("SaveRefreshToken failed: %v", err)
	}

	// 查找 token
	foundUserID, err := repo.FindRefreshToken(ctx, tokenHash)
	if err != nil {
		t.Fatalf("FindRefreshToken failed: %v", err)
	}
	if foundUserID != userID {
		t.Fatalf("expected userID %s, got %s", userID, foundUserID)
	}

	// 不存在的 token
	_, err = repo.FindRefreshToken(ctx, "nonexistent")
	if !errors.Is(err, ErrRefreshTokenNotFound) {
		t.Fatalf("expected ErrRefreshTokenNotFound, got %v", err)
	}

	// 删除 token
	if err := repo.DeleteRefreshToken(ctx, tokenHash); err != nil {
		t.Fatalf("DeleteRefreshToken failed: %v", err)
	}
	_, err = repo.FindRefreshToken(ctx, tokenHash)
	if !errors.Is(err, ErrRefreshTokenNotFound) {
		t.Fatalf("expected ErrRefreshTokenNotFound after delete, got %v", err)
	}
}

func TestUserRepository_DeleteUserRefreshTokens(t *testing.T) {
	repo := newUserRepo()
	ctx := context.Background()

	userID, err := repo.Create(ctx, "eve", "hash_eve")
	if err != nil {
		t.Fatalf("Create failed: %v", err)
	}

	// 保存多个 token
	for i := 0; i < 3; i++ {
		tokenHash := "token_" + string(rune('a'+i))
		expiresAt := time.Now().Add(7 * 24 * time.Hour)
		if err := repo.SaveRefreshToken(ctx, userID, tokenHash, expiresAt); err != nil {
			t.Fatalf("SaveRefreshToken failed: %v", err)
		}
	}

	// 删除用户所有 token
	if err := repo.DeleteUserRefreshTokens(ctx, userID); err != nil {
		t.Fatalf("DeleteUserRefreshTokens failed: %v", err)
	}

	// 验证全部删除
	for i := 0; i < 3; i++ {
		tokenHash := "token_" + string(rune('a'+i))
		_, err := repo.FindRefreshToken(ctx, tokenHash)
		if !errors.Is(err, ErrRefreshTokenNotFound) {
			t.Fatalf("expected ErrRefreshTokenNotFound for token_%c, got %v", 'a'+i, err)
		}
	}
}

func TestUserRepository_ExpiredRefreshToken(t *testing.T) {
	repo := newUserRepo()
	ctx := context.Background()

	userID, err := repo.Create(ctx, "frank", "hash_frank")
	if err != nil {
		t.Fatalf("Create failed: %v", err)
	}

	tokenHash := "expired_token"
	expiresAt := time.Now().Add(-1 * time.Hour) // 已过期

	if err := repo.SaveRefreshToken(ctx, userID, tokenHash, expiresAt); err != nil {
		t.Fatalf("SaveRefreshToken failed: %v", err)
	}

	// 过期 token 应返回 ErrRefreshTokenNotFound
	_, err = repo.FindRefreshToken(ctx, tokenHash)
	if !errors.Is(err, ErrRefreshTokenNotFound) {
		t.Fatalf("expected ErrRefreshTokenNotFound for expired token, got %v", err)
	}
}
