package regionblock

import (
	"context"
	"errors"
	"reflect"
	"testing"

	"hyapp-admin-server/internal/integration/userclient"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

type fakeWhitelistCacheUserClient struct {
	userclient.Client

	called     bool
	err        error
	resolveErr error
	users      map[int64]*userclient.User
	identities map[string]*userclient.UserIdentity
}

func (f *fakeWhitelistCacheUserClient) RefreshLoginRiskWhitelistCache(_ context.Context, req userclient.RefreshLoginRiskWhitelistCacheRequest) (*userclient.LoginRiskWhitelistCacheSnapshot, error) {
	f.called = true
	if req.RequestID != "req-refresh" || req.Caller != "admin-server" {
		return nil, errors.New("unexpected refresh request")
	}
	if f.err != nil {
		return nil, f.err
	}
	return &userclient.LoginRiskWhitelistCacheSnapshot{Refreshed: true, UserWhitelistCount: 2}, nil
}

func (f *fakeWhitelistCacheUserClient) ResolveDisplayUserID(_ context.Context, req userclient.ResolveDisplayUserIDRequest) (*userclient.UserIdentity, error) {
	if req.RequestID != "req-refresh" || req.Caller != "admin-server" {
		return nil, errors.New("unexpected resolve request")
	}
	if f.resolveErr != nil {
		return nil, f.resolveErr
	}
	if identity := f.identities[req.DisplayUserID]; identity != nil {
		return identity, nil
	}
	return nil, status.Error(codes.NotFound, "display user id not found")
}

func (f *fakeWhitelistCacheUserClient) GetUser(_ context.Context, req userclient.GetUserRequest) (*userclient.User, error) {
	if req.RequestID != "req-refresh" || req.Caller != "admin-server" {
		return nil, errors.New("unexpected get user request")
	}
	if user := f.users[req.UserID]; user != nil {
		return user, nil
	}
	return nil, status.Error(codes.NotFound, "user not found")
}

func TestNormalizeUserIDWhitelistResolvesDisplayIDsAndInternalIDs(t *testing.T) {
	service := NewService(nil, &fakeWhitelistCacheUserClient{
		users: map[int64]*userclient.User{
			990000: {UserID: 990000},
		},
		identities: map[string]*userclient.UserIdentity{
			"VIP2026":      {UserID: 900025, DisplayUserID: "VIP2026"},
			"123456789012": {UserID: 900026, DisplayUserID: "123456789012"},
		},
	})
	got, err := service.normalizeUserIDWhitelist(context.Background(), "req-refresh", []string{" VIP2026 ", "123456789012", "990000", "VIP2026"})
	if err != nil {
		t.Fatalf("normalize user whitelist failed: %v", err)
	}
	want := []int64{900025, 900026, 990000}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("normalized user whitelist = %+v, want %+v", got, want)
	}
}

func TestNormalizeUserIDWhitelistRejectsInvalidID(t *testing.T) {
	service := NewService(nil, &fakeWhitelistCacheUserClient{})
	for _, raw := range [][]string{{""}, {"0"}, {"-1"}, {"VIP-2026"}, {"123456"}} {
		if _, err := service.normalizeUserIDWhitelist(context.Background(), "req-refresh", raw); !errors.Is(err, errInvalidPayload) {
			t.Fatalf("normalize %v error = %v, want errInvalidPayload", raw, err)
		}
	}
}

func TestNormalizeUserIDWhitelistKeepsUserServiceError(t *testing.T) {
	upstreamErr := status.Error(codes.Unavailable, "user-service unavailable")
	service := NewService(nil, &fakeWhitelistCacheUserClient{resolveErr: upstreamErr})
	_, err := service.normalizeUserIDWhitelist(context.Background(), "req-refresh", []string{"VIP2026"})
	if !errors.Is(err, upstreamErr) {
		t.Fatalf("normalize error = %v, want upstream error", err)
	}
	if errors.Is(err, errInvalidPayload) {
		t.Fatalf("normalize error = %v, should not be mapped to invalid payload", err)
	}
}

func TestRefreshWhitelistCacheMapsUserServiceError(t *testing.T) {
	client := &fakeWhitelistCacheUserClient{err: errors.New("rpc down")}
	service := NewService(nil, client)
	err := service.refreshWhitelistCache(context.Background(), "req-refresh")
	if !client.called {
		t.Fatal("expected refresh rpc to be called")
	}
	if !errors.Is(err, errWhitelistCacheRefresh) {
		t.Fatalf("refresh error = %v, want errWhitelistCacheRefresh", err)
	}
}
