package auth

import (
	"crypto/rand"
	"fmt"
	"time"

	"golang.org/x/crypto/bcrypt"
)

const (
	OTPLength      = 6
	OTPExpiry      = 10 * time.Minute
	bcryptCost     = bcrypt.DefaultCost
)

// OTP represents a one-time password with its hash and expiry
type OTP struct {
	Hash   string    `json:"hash"`
	Expiry time.Time `json:"expiry"`
}

// GenerateOTP creates a new 6-digit OTP and returns the plaintext and OTP struct
func GenerateOTP() (string, *OTP, error) {
	// Generate random 6-digit number
	otp := generateRandomDigits(OTPLength)
	
	// Hash the OTP
	hash, err := bcrypt.GenerateFromPassword([]byte(otp), bcryptCost)
	if err != nil {
		return "", nil, fmt.Errorf("failed to hash OTP: %w", err)
	}

	return otp, &OTP{
		Hash:   string(hash),
		Expiry: time.Now().Add(OTPExpiry),
	}, nil
}

// VerifyOTP checks if the provided OTP matches the hash and is not expired
func VerifyOTP(otp string, storedOTP *OTP) bool {
	if storedOTP == nil {
		return false
	}

	// Check expiry
	if time.Now().After(storedOTP.Expiry) {
		return false
	}

	// Verify hash
	err := bcrypt.CompareHashAndPassword([]byte(storedOTP.Hash), []byte(otp))
	return err == nil
}

// generateRandomDigits generates n random digits as a string
func generateRandomDigits(n int) string {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		// Fallback to pseudo-random if crypto/rand fails
		return fmt.Sprintf("%06d", time.Now().UnixNano()%1000000)
	}

	var result string
	for i := 0; i < n; i++ {
		result += fmt.Sprintf("%d", b[i]%10)
	}
	return result
}