package usecase_test

import (
	"context"
	"testing"

	memberconfig "gateway/internal/model/member/config"
	"gateway/internal/model/member/domain/enum"
	domusecase "gateway/internal/model/member/domain/usecase"
	"gateway/internal/model/member/repository"
	"gateway/internal/model/member/usecase"

	redislib "gateway/internal/library/redis"

	"github.com/alicebob/miniredis/v2"
	"github.com/stretchr/testify/require"
	"github.com/zeromicro/go-zero/core/stores/redis"
)

func TestOTPUseCase_GenerateAndVerify(t *testing.T) {
	mr := miniredis.RunT(t)
	rds, err := redislib.NewClient(redis.RedisConf{Host: mr.Addr(), Type: testRedisTypeNode})
	require.NoError(t, err)

	uc := usecase.MustOTPUseCase(usecase.OTPUseCaseParam{
		Store:  repository.NewRedisOTPChallengeStore(rds),
		Config: memberconfig.Config{}.Defaults(),
	})

	dto, code, err := uc.Generate(context.Background(), &domusecase.GenerateOTPRequest{
		TenantID: "t1",
		UID:      "u1",
		Purpose:  enum.OTPPurposeBusinessEmail,
		Target:   testUserEmail,
	})
	require.NoError(t, err)
	require.NotEmpty(t, code)

	target, err := uc.Verify(context.Background(), &domusecase.VerifyOTPRequest{
		TenantID:    "t1",
		UID:         "u1",
		ChallengeID: dto.ChallengeID,
		Code:        code,
		Purpose:     enum.OTPPurposeBusinessEmail,
	})
	require.NoError(t, err)
	require.Equal(t, testUserEmail, target)
}

func TestOTPUseCase_VerifyUIDMismatch(t *testing.T) {
	mr := miniredis.RunT(t)
	rds, err := redislib.NewClient(redis.RedisConf{Host: mr.Addr(), Type: testRedisTypeNode})
	require.NoError(t, err)

	uc := usecase.MustOTPUseCase(usecase.OTPUseCaseParam{
		Store:  repository.NewRedisOTPChallengeStore(rds),
		Config: memberconfig.Config{}.Defaults(),
	})

	dto, code, err := uc.Generate(context.Background(), &domusecase.GenerateOTPRequest{
		TenantID: "t1",
		UID:      "victim",
		Purpose:  enum.OTPPurposeBusinessEmail,
		Target:   testUserEmail,
	})
	require.NoError(t, err)

	_, err = uc.Verify(context.Background(), &domusecase.VerifyOTPRequest{
		TenantID:    "t1",
		UID:         "attacker",
		ChallengeID: dto.ChallengeID,
		Code:        code,
		Purpose:     enum.OTPPurposeBusinessEmail,
	})
	require.Error(t, err)
}

func TestOTPUseCase_MaxAttemptsLocks(t *testing.T) {
	mr := miniredis.RunT(t)
	rds, err := redislib.NewClient(redis.RedisConf{Host: mr.Addr(), Type: testRedisTypeNode})
	require.NoError(t, err)

	cfg := memberconfig.Config{}.Defaults()
	cfg.OTP.MaxAttempts = 2

	uc := usecase.MustOTPUseCase(usecase.OTPUseCaseParam{
		Store:  repository.NewRedisOTPChallengeStore(rds),
		Config: cfg,
	})

	dto, _, err := uc.Generate(context.Background(), &domusecase.GenerateOTPRequest{
		TenantID: "t1",
		UID:      "u1",
		Purpose:  enum.OTPPurposeBusinessEmail,
		Target:   testUserEmail,
	})
	require.NoError(t, err)

	for range cfg.OTP.MaxAttempts {
		_, err = uc.Verify(context.Background(), &domusecase.VerifyOTPRequest{
			TenantID:    "t1",
			UID:         "u1",
			ChallengeID: dto.ChallengeID,
			Code:        "000000",
			Purpose:     enum.OTPPurposeBusinessEmail,
		})
		require.Error(t, err)
	}

	_, err = uc.Verify(context.Background(), &domusecase.VerifyOTPRequest{
		TenantID:    "t1",
		UID:         "u1",
		ChallengeID: dto.ChallengeID,
		Code:        "000000",
		Purpose:     enum.OTPPurposeBusinessEmail,
	})
	require.Error(t, err)
}
