package gateway

import (
	"fmt"

	"go.uber.org/zap"

	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/security/password"
	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/security/securestring"
)

// VerifyPasswordUseCase verifies a password against a hash
type VerifyPasswordUseCase struct {
	passwordProvider password.PasswordProvider
	logger           *zap.Logger
}

// ProvideVerifyPasswordUseCase creates a new VerifyPasswordUseCase
func ProvideVerifyPasswordUseCase(
	passwordProvider password.PasswordProvider,
	logger *zap.Logger,
) *VerifyPasswordUseCase {
	return &VerifyPasswordUseCase{
		passwordProvider: passwordProvider,
		logger:           logger.Named("verify-password-usecase"),
	}
}

// Execute verifies a plain password against a hashed password
// Returns ErrInvalidCredentials if password doesn't match (for security)
func (uc *VerifyPasswordUseCase) Execute(plainPassword, passwordHash string) error {
	// Create secure string from password
	securePassword, err := securestring.NewSecureString(plainPassword)
	if err != nil {
		uc.logger.Error("failed to create secure password", zap.Error(err))
		return fmt.Errorf("failed to process password: %w", err)
	}
	defer securePassword.Wipe() // Clean up password from memory

	// Verify password
	match, err := uc.passwordProvider.ComparePasswordAndHash(securePassword, passwordHash)
	if err != nil {
		uc.logger.Error("failed to compare password and hash", zap.Error(err))
		return fmt.Errorf("failed to verify password: %w", err)
	}

	if !match {
		uc.logger.Debug("password verification failed")
		return ErrInvalidCredentials
	}

	uc.logger.Debug("password verified successfully")
	return nil
}

// ExecuteTimingSafe verifies a password in a timing-safe manner
// CWE-208: Observable Timing Discrepancy - Prevents user enumeration via timing attacks
//
// This method ALWAYS performs password hashing, even when the user doesn't exist,
// to ensure constant-time behavior regardless of whether the email exists in the system.
//
// Parameters:
// - plainPassword: The password to verify
// - passwordHash: The hash to compare against (empty string if user doesn't exist)
// - userExists: Whether the user exists in the system
//
// Returns ErrInvalidCredentials if verification fails for any reason
func (uc *VerifyPasswordUseCase) ExecuteTimingSafe(plainPassword, passwordHash string, userExists bool) error {
	// Create secure string from password
	securePassword, err := securestring.NewSecureString(plainPassword)
	if err != nil {
		uc.logger.Error("failed to create secure password", zap.Error(err))
		return fmt.Errorf("failed to process password: %w", err)
	}
	defer securePassword.Wipe() // Clean up password from memory

	if !userExists || passwordHash == "" {
		// User doesn't exist or no password hash available
		// Perform dummy password hashing to maintain constant time
		uc.logger.Debug("performing timing-safe dummy password verification")
		match, err := uc.passwordProvider.ComparePasswordAndHash(securePassword, password.DummyPasswordHash)
		if err != nil {
			// Even if dummy verification fails, we don't care about the error
			// The important part is that we spent the same amount of time
			uc.logger.Debug("dummy password verification completed", zap.Error(err))
		}
		_ = match // Explicitly ignore the result
		return ErrInvalidCredentials
	}

	// User exists - perform real password verification
	match, err := uc.passwordProvider.ComparePasswordAndHash(securePassword, passwordHash)
	if err != nil {
		uc.logger.Error("failed to compare password and hash", zap.Error(err))
		return ErrInvalidCredentials
	}

	if !match {
		uc.logger.Debug("password verification failed")
		return ErrInvalidCredentials
	}

	uc.logger.Debug("password verified successfully")
	return nil
}
