package identity

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/core/authidentity"
	"github.com/tidbcloud/AgentTeam/internal/core/secrets"
)

func TestVerifyMobileTokenInvalidatesAfterWrongCodeLimit(t *testing.T) {
	t.Parallel()

	ctx := context.Background()
	db := startIdentityTestPool(t, ctx)
	store := New(db, Options{})

	now := time.Now().UTC()
	userID := "usr_mobile_lockout"
	mustExec(t, db, `
INSERT INTO users (id, email, name, display_name, avatar_url, is_admin, password_hash, email_verified_at, created_at, updated_at)
VALUES ($1, 'mobile-lockout@example.com', 'mobile-lockout', 'Mobile Lockout', '', FALSE, 'secret_hash', NULL, $2, $2)
`, userID, now)

	code := "246810"
	codeHash, err := authidentity.HashMobileVerificationCode(code)
	require.NoError(t, err)
	require.NoError(t, store.CreateMobileVerificationToken(ctx, userID, "+15551234567", codeHash, now.Add(15*time.Minute)))

	for attempt := 1; attempt <= mobileVerificationMaxFailedAttempts; attempt++ {
		_, err := store.VerifyMobileToken(ctx, userID, "000000")
		require.ErrorContains(t, err, "invalid mobile verification code")
	}

	var failedAttempts int
	var invalidatedAt *time.Time
	require.NoError(t, db.QueryRow(ctx, `
SELECT failed_attempts, invalidated_at
FROM user_mobile_verification_tokens
WHERE user_id = $1
`, userID).Scan(&failedAttempts, &invalidatedAt))
	require.Equal(t, mobileVerificationMaxFailedAttempts, failedAttempts)
	require.NotNil(t, invalidatedAt)

	activeCode, hasActiveCode, err := store.GetActiveMobileVerificationCodeForUser(ctx, userID)
	require.NoError(t, err)
	require.False(t, hasActiveCode)
	require.Empty(t, activeCode.Mobile)

	_, err = store.VerifyMobileToken(ctx, userID, code)
	require.ErrorContains(t, err, "invalid mobile verification code")

	var mobile string
	var mobileVerifiedAt *time.Time
	require.NoError(t, db.QueryRow(ctx, `
SELECT COALESCE(mobile, ''), mobile_verified_at
FROM users
WHERE id = $1
`, userID).Scan(&mobile, &mobileVerifiedAt))
	require.Empty(t, mobile)
	require.Nil(t, mobileVerifiedAt)
}

func TestVerifyMobileTokenAcceptsLegacySHA256Hash(t *testing.T) {
	t.Parallel()

	ctx := context.Background()
	db := startIdentityTestPool(t, ctx)
	store := New(db, Options{})

	now := time.Now().UTC()
	userID := "usr_mobile_legacy_hash"
	mustExec(t, db, `
INSERT INTO users (id, email, name, display_name, avatar_url, is_admin, password_hash, email_verified_at, created_at, updated_at)
VALUES ($1, 'mobile-legacy@example.com', 'mobile-legacy', 'Mobile Legacy', '', FALSE, 'secret_hash', NULL, $2, $2)
`, userID, now)

	code := "135790"
	require.NoError(t, store.CreateMobileVerificationToken(ctx, userID, "+15557654321", secrets.HashToken(code), now.Add(15*time.Minute)))

	verifiedUser, err := store.VerifyMobileToken(ctx, userID, code)
	require.NoError(t, err)
	require.Equal(t, "+15557654321", verifiedUser.Mobile)
	require.True(t, verifiedUser.HasVerifiedMobile())
}
