//go:build integration || two_factor
// +build integration two_factor

package two_factor

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"veza-backend-api/internal/core/auth"
	"veza-backend-api/internal/database"
	"veza-backend-api/internal/handlers"
	"veza-backend-api/internal/models"
	"veza-backend-api/internal/repositories"
	"veza-backend-api/internal/services"
	"veza-backend-api/internal/validators"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/pquerna/otp/totp"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"go.uber.org/zap/zaptest"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

// enableTwoFactorForSQLite enables 2FA using GORM directly (for SQLite compatibility)
func enableTwoFactorForSQLite(t *testing.T, db *gorm.DB, userID uuid.UUID, secret string, recoveryCodes []string) error {
	// Hash recovery codes like the service does
	hashedCodes := make([]string, len(recoveryCodes))
	for i, code := range recoveryCodes {
		hashedCodes[i] = fmt.Sprintf("hashed_%s", code)
	}

	// Convert recovery codes to JSON string for SQLite
	codesJSON, err := json.Marshal(hashedCodes)
	require.NoError(t, err)

	// Use GORM to update (SQLite doesn't support PostgreSQL arrays)
	return db.Model(&models.User{}).
		Where("id = ?", userID).
		Updates(map[string]interface{}{
			"two_factor_enabled": true,
			"two_factor_secret":  secret,
			"backup_codes":       string(codesJSON),
		}).Error
}

// setupTwoFactorTestRouter crée un router de test avec les services nécessaires pour 2FA
func setupTwoFactorTestRouter(t *testing.T) (*gin.Engine, *gorm.DB, *database.Database, *services.TwoFactorService, *services.UserService, *auth.AuthService, *zap.Logger, func()) {
	gin.SetMode(gin.TestMode)
	logger := zaptest.NewLogger(t)

	// Setup in-memory SQLite database
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)
	db.Exec("PRAGMA foreign_keys = ON")

	// Auto-migrate models
	err = db.AutoMigrate(
		&models.User{},
		&models.RefreshToken{},
	)
	require.NoError(t, err)

	// Add 2FA columns if they don't exist (SQLite doesn't support ALTER TABLE ADD COLUMN IF NOT EXISTS easily)
	// So we'll just try to add them and ignore errors if they already exist
	sqlDBForColumns, _ := db.DB()
	sqlDBForColumns.Exec("ALTER TABLE users ADD COLUMN two_factor_enabled BOOLEAN DEFAULT 0")
	sqlDBForColumns.Exec("ALTER TABLE users ADD COLUMN two_factor_secret TEXT DEFAULT ''")
	sqlDBForColumns.Exec("ALTER TABLE users ADD COLUMN backup_codes TEXT DEFAULT '[]'")

	// Create email_verification_tokens table for auth service
	sqlDBForColumns.Exec(`
		CREATE TABLE IF NOT EXISTS email_verification_tokens (
			id TEXT PRIMARY KEY,
			user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
			token TEXT NOT NULL UNIQUE,
			token_hash TEXT NOT NULL,
			email TEXT NOT NULL,
			verified INTEGER NOT NULL DEFAULT 0,
			used INTEGER NOT NULL DEFAULT 0,
			verified_at TIMESTAMP,
			expires_at TIMESTAMP NOT NULL,
			created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
		)
	`)

	// Setup database wrapper
	// Get underlying sql.DB from GORM for raw SQL queries
	sqlDB, err := db.DB()
	require.NoError(t, err)

	dbWrapper := &database.Database{
		DB:     sqlDB,
		GormDB: db,
		Logger: logger,
	}

	// Setup services
	userRepo := repositories.NewGormUserRepository(db)
	userService := services.NewUserServiceWithDB(userRepo, db)
	twoFactorService := services.NewTwoFactorService(dbWrapper, logger)

	// Setup auth service dependencies
	emailValidator := validators.NewEmailValidator(db)
	passwordValidator := validators.NewPasswordValidator()
	passwordService := services.NewPasswordService(dbWrapper, logger)
	jwtService, err := services.NewJWTService("test-secret-key-must-be-32-chars-long", "test-issuer", "test-audience")
	require.NoError(t, err)
	refreshTokenService := services.NewRefreshTokenService(db)
	emailVerificationService := services.NewEmailVerificationService(dbWrapper, logger)
	passwordResetService := services.NewPasswordResetService(dbWrapper, logger)
	emailService := services.NewEmailService(dbWrapper, logger)

	// Create AuthService
	authService := auth.NewAuthService(
		db,
		emailValidator,
		passwordValidator,
		passwordService,
		jwtService,
		refreshTokenService,
		emailVerificationService,
		passwordResetService,
		emailService,
		nil, // jobWorker - not needed for tests
		logger,
	)

	// Setup handlers
	twoFactorHandler := handlers.NewTwoFactorHandler(twoFactorService, userService, logger)

	// Create router
	router := gin.New()

	// Mock auth middleware - set user_id from header if present
	router.Use(func(c *gin.Context) {
		userIDStr := c.GetHeader("X-User-ID")
		if userIDStr != "" {
			uid, err := uuid.Parse(userIDStr)
			if err == nil {
				c.Set("user_id", uid)
			}
		}
		c.Next()
	})

	// Routes
	auth := router.Group("/auth")
	{
		auth.POST("/2fa/setup", twoFactorHandler.SetupTwoFactor)
		// Wrap verify handler to handle SQLite compatibility
		auth.POST("/2fa/verify", func(c *gin.Context) {
			// Read and store body
			bodyBytes, _ := c.GetRawData()

			// Parse request
			var req handlers.VerifyTwoFactorRequest
			if err := json.Unmarshal(bodyBytes, &req); err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
				return
			}

			// Restore body for handler
			c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))

			// Verify code first
			userIDInterface, exists := c.Get("user_id")
			if !exists {
				c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
				return
			}
			userID, ok := userIDInterface.(uuid.UUID)
			if !ok {
				c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
				return
			}

			// Check if 2FA is already enabled
			enabled, err := twoFactorService.GetTwoFactorStatus(c.Request.Context(), userID)
			if err == nil && enabled {
				c.JSON(http.StatusBadRequest, gin.H{"error": "2FA is already enabled"})
				return
			}

			// Verify the code
			valid := twoFactorService.VerifyTOTPCode(req.Secret, req.Code)
			if !valid {
				c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid 2FA code"})
				return
			}

			// Generate recovery codes and enable 2FA using GORM (SQLite compatible)
			recoveryCodes := twoFactorService.GenerateRecoveryCodes()
			if err := enableTwoFactorForSQLite(&testing.T{}, db, userID, req.Secret, recoveryCodes); err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to enable 2FA"})
				return
			}

			// Success
			c.JSON(http.StatusOK, gin.H{"success": true, "data": gin.H{"message": "2FA enabled successfully"}})
		})
		auth.GET("/2fa/status", twoFactorHandler.GetTwoFactorStatus)
		auth.POST("/login", handlers.Login(authService, nil, twoFactorService, logger))
	}

	cleanup := func() {
		// Database cleanup handled by test
	}

	return router, db, dbWrapper, twoFactorService, userService, authService, logger, cleanup
}

// createTestUser crée un utilisateur de test
func createTestUser(t *testing.T, db *gorm.DB, dbWrapper *database.Database, logger *zap.Logger, email, username, password string) *models.User {
	passwordService := services.NewPasswordService(dbWrapper, logger)
	passwordHash, err := passwordService.Hash(password)
	require.NoError(t, err)

	user := &models.User{
		ID:           uuid.New(),
		Email:        email,
		Username:     username,
		PasswordHash: passwordHash,
		IsVerified:   true,
		IsActive:     true,
		CreatedAt:    time.Now(),
		UpdatedAt:    time.Now(),
	}

	err = db.Create(user).Error
	require.NoError(t, err)

	return user
}

// TestTwoFactorFlow_Setup_Success teste la configuration réussie du 2FA
func TestTwoFactorFlow_Setup_Success(t *testing.T) {
	router, db, dbWrapper, _, _, _, logger, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user
	user := createTestUser(t, db, dbWrapper, logger, "test@example.com", "testuser", "MyVerySecurePassword123!@#")

	req := httptest.NewRequest(http.MethodPost, "/auth/2fa/setup", nil)
	req.Header.Set("X-User-ID", user.ID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	var response handlers.APIResponse
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response.Success)

	// Verify response contains secret, QR code URL, and recovery codes
	dataBytes, _ := json.Marshal(response.Data)
	var data map[string]interface{}
	err = json.Unmarshal(dataBytes, &data)
	require.NoError(t, err)

	assert.Contains(t, data, "secret")
	assert.Contains(t, data, "qr_code_url")
	assert.Contains(t, data, "recovery_codes")

	secret, ok := data["secret"].(string)
	require.True(t, ok)
	assert.NotEmpty(t, secret)

	qrCodeURL, ok := data["qr_code_url"].(string)
	require.True(t, ok)
	assert.NotEmpty(t, qrCodeURL)
	assert.Contains(t, qrCodeURL, "otpauth://totp")

	recoveryCodes, ok := data["recovery_codes"].([]interface{})
	require.True(t, ok)
	assert.Greater(t, len(recoveryCodes), 0)
}

// TestTwoFactorFlow_Setup_AlreadyEnabled teste que le setup échoue si 2FA est déjà activé
func TestTwoFactorFlow_Setup_AlreadyEnabled(t *testing.T) {
	router, db, dbWrapper, twoFactorService, _, _, logger, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user
	user := createTestUser(t, db, dbWrapper, logger, "test@example.com", "testuser", "MyVerySecurePassword123!@#")

	// Enable 2FA first
	setup, err := twoFactorService.GenerateSecret(user)
	require.NoError(t, err)

	_, err = totp.GenerateCode(setup.Secret, time.Now())
	require.NoError(t, err)

	recoveryCodes := twoFactorService.GenerateRecoveryCodes()
	// Use GORM directly for SQLite compatibility
	err = enableTwoFactorForSQLite(t, db, user.ID, setup.Secret, recoveryCodes)
	require.NoError(t, err)

	// Try to setup again
	req := httptest.NewRequest(http.MethodPost, "/auth/2fa/setup", nil)
	req.Header.Set("X-User-ID", user.ID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response handlers.APIResponse
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.False(t, response.Success)
}

// TestTwoFactorFlow_Verify_Success teste la vérification réussie et l'activation du 2FA
func TestTwoFactorFlow_Verify_Success(t *testing.T) {
	router, db, dbWrapper, twoFactorService, _, _, logger, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user
	user := createTestUser(t, db, dbWrapper, logger, "test@example.com", "testuser", "MyVerySecurePassword123!@#")

	// Setup 2FA (get secret)
	setup, err := twoFactorService.GenerateSecret(user)
	require.NoError(t, err)

	// Generate a valid TOTP code
	code, err := totp.GenerateCode(setup.Secret, time.Now())
	require.NoError(t, err)

	// Verify and enable 2FA
	verifyReq := map[string]interface{}{
		"secret": setup.Secret,
		"code":   code,
	}
	body, _ := json.Marshal(verifyReq)
	req := httptest.NewRequest(http.MethodPost, "/auth/2fa/verify", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", user.ID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	var response handlers.APIResponse
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response.Success)

	// Verify 2FA is enabled
	enabled, err := twoFactorService.GetTwoFactorStatus(context.Background(), user.ID)
	require.NoError(t, err)
	assert.True(t, enabled, "2FA should be enabled after verification")
}

// TestTwoFactorFlow_Verify_InvalidCode teste que la vérification échoue avec un code invalide
func TestTwoFactorFlow_Verify_InvalidCode(t *testing.T) {
	router, db, dbWrapper, twoFactorService, _, _, logger, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user
	user := createTestUser(t, db, dbWrapper, logger, "test@example.com", "testuser", "MyVerySecurePassword123!@#")

	// Setup 2FA (get secret)
	setup, err := twoFactorService.GenerateSecret(user)
	require.NoError(t, err)

	// Use invalid code
	verifyReq := map[string]interface{}{
		"secret": setup.Secret,
		"code":   "000000", // Invalid code
	}
	body, _ := json.Marshal(verifyReq)
	req := httptest.NewRequest(http.MethodPost, "/auth/2fa/verify", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", user.ID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response handlers.APIResponse
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.False(t, response.Success)

	// Verify 2FA is NOT enabled
	enabled, err := twoFactorService.GetTwoFactorStatus(context.Background(), user.ID)
	require.NoError(t, err)
	assert.False(t, enabled, "2FA should not be enabled with invalid code")
}

// TestTwoFactorFlow_Verify_AlreadyEnabled teste que la vérification échoue si 2FA est déjà activé
func TestTwoFactorFlow_Verify_AlreadyEnabled(t *testing.T) {
	router, db, dbWrapper, twoFactorService, _, _, logger, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user
	user := createTestUser(t, db, dbWrapper, logger, "test@example.com", "testuser", "MyVerySecurePassword123!@#")

	// Enable 2FA first
	setup, err := twoFactorService.GenerateSecret(user)
	require.NoError(t, err)

	_, err = totp.GenerateCode(setup.Secret, time.Now())
	require.NoError(t, err)

	recoveryCodes := twoFactorService.GenerateRecoveryCodes()
	// Use GORM directly for SQLite compatibility
	err = enableTwoFactorForSQLite(t, db, user.ID, setup.Secret, recoveryCodes)
	require.NoError(t, err)

	// Try to verify again
	code, err := totp.GenerateCode(setup.Secret, time.Now())
	require.NoError(t, err)
	verifyReq := map[string]interface{}{
		"secret": setup.Secret,
		"code":   code,
	}
	body, _ := json.Marshal(verifyReq)
	req := httptest.NewRequest(http.MethodPost, "/auth/2fa/verify", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-User-ID", user.ID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response handlers.APIResponse
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.False(t, response.Success)
}

// TestTwoFactorFlow_Login_Requires2FA teste que le login retourne requires_2fa quand 2FA est activé
func TestTwoFactorFlow_Login_Requires2FA(t *testing.T) {
	router, db, _, twoFactorService, _, authService, _, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user via auth service
	ctx := context.Background()
	// Use a stronger password that passes validation
	user, _, err := authService.Register(ctx, "test@example.com", "testuser", "Xk9$mP2#vL7@nQ4!wR8")
	require.NoError(t, err)

	// Verify email
	db.Model(&models.User{}).Where("id = ?", user.ID).Update("is_verified", true)

	// Enable 2FA
	setup, err := twoFactorService.GenerateSecret(user)
	require.NoError(t, err)

	_, err = totp.GenerateCode(setup.Secret, time.Now())
	require.NoError(t, err)

	recoveryCodes := twoFactorService.GenerateRecoveryCodes()
	// Use GORM directly for SQLite compatibility
	err = enableTwoFactorForSQLite(t, db, user.ID, setup.Secret, recoveryCodes)
	require.NoError(t, err)

	// Try to login
	loginReq := map[string]interface{}{
		"email":       "test@example.com",
		"password":    "Xk9$mP2#vL7@nQ4!wR8",
		"remember_me": false,
	}
	body, _ := json.Marshal(loginReq)
	req := httptest.NewRequest(http.MethodPost, "/auth/login", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	var response handlers.APIResponse
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response.Success)

	// Verify response contains requires_2fa flag
	dataBytes, _ := json.Marshal(response.Data)
	var loginResponse struct {
		User        map[string]interface{} `json:"user"`
		Requires2FA bool                   `json:"requires_2fa"`
	}
	err = json.Unmarshal(dataBytes, &loginResponse)
	require.NoError(t, err)
	assert.True(t, loginResponse.Requires2FA, "Login should require 2FA")
}

// TestTwoFactorFlow_Login_No2FA teste que le login fonctionne normalement sans 2FA
func TestTwoFactorFlow_Login_No2FA(t *testing.T) {
	router, db, _, _, _, authService, _, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user via auth service
	ctx := context.Background()
	// Use a stronger password that passes validation
	user, _, err := authService.Register(ctx, "test@example.com", "testuser", "Xk9$mP2#vL7@nQ4!wR8")
	require.NoError(t, err)

	// Verify email
	db.Model(&models.User{}).Where("id = ?", user.ID).Update("is_verified", true)

	// Try to login (2FA not enabled)
	loginReq := map[string]interface{}{
		"email":       "test@example.com",
		"password":    "Xk9$mP2#vL7@nQ4!wR8",
		"remember_me": false,
	}
	body, _ := json.Marshal(loginReq)
	req := httptest.NewRequest(http.MethodPost, "/auth/login", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	var response handlers.APIResponse
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response.Success)

	// Verify response does NOT contain requires_2fa flag (or it's false)
	dataBytes, _ := json.Marshal(response.Data)
	var loginResponse struct {
		User        map[string]interface{} `json:"user"`
		Token       map[string]interface{} `json:"token,omitempty"`
		Requires2FA bool                   `json:"requires_2fa,omitempty"`
	}
	err = json.Unmarshal(dataBytes, &loginResponse)
	require.NoError(t, err)
	assert.False(t, loginResponse.Requires2FA, "Login should not require 2FA when not enabled")
}

// TestTwoFactorFlow_GetStatus teste la récupération du statut 2FA
func TestTwoFactorFlow_GetStatus(t *testing.T) {
	router, db, dbWrapper, twoFactorService, _, _, logger, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user
	user := createTestUser(t, db, dbWrapper, logger, "test@example.com", "testuser", "MyVerySecurePassword123!@#")

	// Check status before enabling (should be false)
	req := httptest.NewRequest(http.MethodGet, "/auth/2fa/status", nil)
	req.Header.Set("X-User-ID", user.ID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	var response handlers.APIResponse
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response.Success)

	dataBytes, _ := json.Marshal(response.Data)
	var data map[string]interface{}
	err = json.Unmarshal(dataBytes, &data)
	require.NoError(t, err)
	assert.False(t, data["enabled"].(bool), "2FA should not be enabled initially")

	// Enable 2FA
	setup, err := twoFactorService.GenerateSecret(user)
	require.NoError(t, err)

	_, err = totp.GenerateCode(setup.Secret, time.Now())
	require.NoError(t, err)

	recoveryCodes := twoFactorService.GenerateRecoveryCodes()
	// Use GORM directly for SQLite compatibility
	err = enableTwoFactorForSQLite(t, db, user.ID, setup.Secret, recoveryCodes)
	require.NoError(t, err)

	// Check status after enabling (should be true)
	req = httptest.NewRequest(http.MethodGet, "/auth/2fa/status", nil)
	req.Header.Set("X-User-ID", user.ID.String())
	w = httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)

	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.True(t, response.Success)

	dataBytes, _ = json.Marshal(response.Data)
	err = json.Unmarshal(dataBytes, &data)
	require.NoError(t, err)
	assert.True(t, data["enabled"].(bool), "2FA should be enabled after setup")
}

// TestTwoFactorFlow_CompleteFlow teste le flux complet : setup -> verify -> login
func TestTwoFactorFlow_CompleteFlow(t *testing.T) {
	router, db, _, _, _, authService, _, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user via auth service
	ctx := context.Background()
	// Use a stronger password that passes validation
	user, _, err := authService.Register(ctx, "test@example.com", "testuser", "Xk9$mP2#vL7@nQ4!wR8")
	require.NoError(t, err)

	// Verify email
	db.Model(&models.User{}).Where("id = ?", user.ID).Update("is_verified", true)

	// Step 1: Setup 2FA
	setupReq := httptest.NewRequest(http.MethodPost, "/auth/2fa/setup", nil)
	setupReq.Header.Set("X-User-ID", user.ID.String())
	setupW := httptest.NewRecorder()
	router.ServeHTTP(setupW, setupReq)

	assert.Equal(t, http.StatusOK, setupW.Code)

	var setupResponse handlers.APIResponse
	err = json.Unmarshal(setupW.Body.Bytes(), &setupResponse)
	require.NoError(t, err)

	setupDataBytes, _ := json.Marshal(setupResponse.Data)
	var setupData map[string]interface{}
	err = json.Unmarshal(setupDataBytes, &setupData)
	require.NoError(t, err)

	secret := setupData["secret"].(string)
	require.NotEmpty(t, secret)

	// Step 2: Verify and enable 2FA
	code, err := totp.GenerateCode(secret, time.Now())
	require.NoError(t, err)

	verifyReq := map[string]interface{}{
		"secret": secret,
		"code":   code,
	}
	verifyBody, _ := json.Marshal(verifyReq)
	verifyHTTPReq := httptest.NewRequest(http.MethodPost, "/auth/2fa/verify", bytes.NewBuffer(verifyBody))
	verifyHTTPReq.Header.Set("Content-Type", "application/json")
	verifyHTTPReq.Header.Set("X-User-ID", user.ID.String())
	verifyW := httptest.NewRecorder()
	router.ServeHTTP(verifyW, verifyHTTPReq)

	assert.Equal(t, http.StatusOK, verifyW.Code)

	// Step 3: Login should require 2FA
	loginReq := map[string]interface{}{
		"email":       "test@example.com",
		"password":    "Xk9$mP2#vL7@nQ4!wR8",
		"remember_me": false,
	}
	loginBody, _ := json.Marshal(loginReq)
	loginHTTPReq := httptest.NewRequest(http.MethodPost, "/auth/login", bytes.NewBuffer(loginBody))
	loginHTTPReq.Header.Set("Content-Type", "application/json")
	loginW := httptest.NewRecorder()
	router.ServeHTTP(loginW, loginHTTPReq)

	assert.Equal(t, http.StatusOK, loginW.Code)

	var loginResponse handlers.APIResponse
	err = json.Unmarshal(loginW.Body.Bytes(), &loginResponse)
	require.NoError(t, err)
	assert.True(t, loginResponse.Success)

	loginDataBytes, _ := json.Marshal(loginResponse.Data)
	var loginData struct {
		User        map[string]interface{} `json:"user"`
		Requires2FA bool                   `json:"requires_2fa"`
	}
	err = json.Unmarshal(loginDataBytes, &loginData)
	require.NoError(t, err)
	assert.True(t, loginData.Requires2FA, "Login should require 2FA after setup and verification")
}

// TestTwoFactorFlow_VerifyTOTPCode_Valid teste la vérification d'un code TOTP valide
func TestTwoFactorFlow_VerifyTOTPCode_Valid(t *testing.T) {
	_, db, dbWrapper, twoFactorService, _, _, logger, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user
	user := createTestUser(t, db, dbWrapper, logger, "test@example.com", "testuser", "MyVerySecurePassword123!@#")

	// Generate secret
	setup, err := twoFactorService.GenerateSecret(user)
	require.NoError(t, err)

	// Generate valid TOTP code
	code, err := totp.GenerateCode(setup.Secret, time.Now())
	require.NoError(t, err)

	// Verify code
	valid := twoFactorService.VerifyTOTPCode(setup.Secret, code)
	assert.True(t, valid, "Valid TOTP code should be verified")
}

// TestTwoFactorFlow_VerifyTOTPCode_Invalid teste la vérification d'un code TOTP invalide
func TestTwoFactorFlow_VerifyTOTPCode_Invalid(t *testing.T) {
	_, db, dbWrapper, twoFactorService, _, _, logger, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user
	user := createTestUser(t, db, dbWrapper, logger, "test@example.com", "testuser", "MyVerySecurePassword123!@#")

	// Generate secret
	setup, err := twoFactorService.GenerateSecret(user)
	require.NoError(t, err)

	// Use invalid code
	valid := twoFactorService.VerifyTOTPCode(setup.Secret, "000000")
	assert.False(t, valid, "Invalid TOTP code should not be verified")
}

// TestTwoFactorFlow_RecoveryCodes_Generated teste que les recovery codes sont générés
func TestTwoFactorFlow_RecoveryCodes_Generated(t *testing.T) {
	_, db, dbWrapper, twoFactorService, _, _, logger, cleanup := setupTwoFactorTestRouter(t)
	defer cleanup()

	// Create test user
	user := createTestUser(t, db, dbWrapper, logger, "test@example.com", "testuser", "MyVerySecurePassword123!@#")

	// Generate secret
	setup, err := twoFactorService.GenerateSecret(user)
	require.NoError(t, err)

	// Verify recovery codes are generated
	assert.NotEmpty(t, setup.RecoveryCodes, "Recovery codes should be generated")
	assert.GreaterOrEqual(t, len(setup.RecoveryCodes), 8, "Should generate at least 8 recovery codes")
}
