package auth

import (
	"fmt"
	"log"
	"sync"
	"time"
)

// AuthService provides authentication functionality
type AuthService struct {
	otpStore      OTPStore
	smtpSender    *SMTPSender
	sessionManager *SessionManager
	mu            sync.RWMutex
}

// OTPStore defines the interface for storing and retrieving OTPs
type OTPStore interface {
	StoreOTP(email, otp string, expiry time.Time) error
	GetOTP(email string) (string, time.Time, error)
	DeleteOTP(email string) error
}

// InMemoryOTPStore is a simple in-memory OTP store for development
type InMemoryOTPStore struct {
	store map[string]struct {
		otp    string
		expiry time.Time
	}
	mu sync.RWMutex
}

// NewInMemoryOTPStore creates a new in-memory OTP store
func NewInMemoryOTPStore() *InMemoryOTPStore {
	return &InMemoryOTPStore{
		store: make(map[string]struct {
			otp    string
			expiry time.Time
		}),
	}
}

// StoreOTP stores an OTP for the given email
func (s *InMemoryOTPStore) StoreOTP(email, otp string, expiry time.Time) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	
	s.store[email] = struct {
		otp    string
		expiry time.Time
	}{otp: otp, expiry: expiry}
	
	return nil
}

// GetOTP retrieves the OTP for the given email
func (s *InMemoryOTPStore) GetOTP(email string) (string, time.Time, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	
	entry, exists := s.store[email]
	if !exists {
		return "", time.Time{}, fmt.Errorf("OTP not found for email: %s", email)
	}
	
	return entry.otp, entry.expiry, nil
}

// DeleteOTP removes the OTP for the given email
func (s *InMemoryOTPStore) DeleteOTP(email string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	
	delete(s.store, email)
	return nil
}

// NewAuthService creates a new authentication service
func NewAuthService(smtpSender *SMTPSender, sessionManager *SessionManager, otpStore OTPStore) *AuthService {
	return &AuthService{
		otpStore:      otpStore,
		smtpSender:    smtpSender,
		sessionManager: sessionManager,
	}
}

// RequestOTP generates and sends an OTP to the given email
func (as *AuthService) RequestOTP(email string) error {
	// Generate OTP
	otpPlain, otp, err := GenerateOTP()
	if err != nil {
		return fmt.Errorf("failed to generate OTP: %w", err)
	}

	// Store OTP hash and expiry
	err = as.otpStore.StoreOTP(email, otp.Hash, otp.Expiry)
	if err != nil {
		return fmt.Errorf("failed to store OTP: %w", err)
	}

	// Send OTP via email
	err = as.smtpSender.SendOTP(email, otpPlain)
	if err != nil {
		log.Printf("OTP for %s: %s (SMTP failed: %v)", email, otpPlain, err)
		// Keep the OTP stored so developer can use it for testing
		return nil // Don't fail — allow dev access via logs
	}

	log.Printf("OTP sent to %s", email)
	return nil
}

// VerifyOTP verifies the provided OTP for the given email
func (as *AuthService) VerifyOTP(email, userOTP string) (bool, error) {
	// Retrieve stored OTP
	storedOTPHash, expiry, err := as.otpStore.GetOTP(email)
	if err != nil {
		return false, fmt.Errorf("OTP not found or expired: %w", err)
	}

	// Create OTP struct for verification
	storedOTP := &OTP{
		Hash:   storedOTPHash,
		Expiry: expiry,
	}

	// Verify OTP
	if !VerifyOTP(userOTP, storedOTP) {
		return false, nil
	}

	// Clean up OTP after successful verification
	as.otpStore.DeleteOTP(email)
	return true, nil
}

// GetSessionManager returns the session manager
func (as *AuthService) GetSessionManager() *SessionManager {
	return as.sessionManager
}