package middleware

import (
	"sync"
	"time"

	"github.com/gofiber/fiber/v2"
)

// UserRateLimiter implements per-user rate limiting
type UserRateLimiter struct {
	requests map[uint][]time.Time
	mu       sync.RWMutex
	max      int
	window   time.Duration
}

// NewUserRateLimiter creates a new per-user rate limiter
func NewUserRateLimiter(max int, window time.Duration) *UserRateLimiter {
	limiter := &UserRateLimiter{
		requests: make(map[uint][]time.Time),
		max:      max,
		window:   window,
	}

	// Start cleanup goroutine
	go limiter.cleanup()

	return limiter
}

// Allow checks if a request should be allowed
func (l *UserRateLimiter) Allow(userID uint) bool {
	l.mu.Lock()
	defer l.mu.Unlock()

	now := time.Now()
	cutoff := now.Add(-l.window)

	// Get user's request history
	requests, exists := l.requests[userID]
	if !exists {
		l.requests[userID] = []time.Time{now}
		return true
	}

	// Filter out old requests
	var validRequests []time.Time
	for _, reqTime := range requests {
		if reqTime.After(cutoff) {
			validRequests = append(validRequests, reqTime)
		}
	}

	// Check if limit exceeded
	if len(validRequests) >= l.max {
		l.requests[userID] = validRequests
		return false
	}

	// Add current request
	validRequests = append(validRequests, now)
	l.requests[userID] = validRequests
	return true
}

// cleanup removes old entries periodically
func (l *UserRateLimiter) cleanup() {
	ticker := time.NewTicker(5 * time.Minute)
	defer ticker.Stop()

	for range ticker.C {
		l.mu.Lock()
		now := time.Now()
		cutoff := now.Add(-l.window)

		for userID, requests := range l.requests {
			var validRequests []time.Time
			for _, reqTime := range requests {
				if reqTime.After(cutoff) {
					validRequests = append(validRequests, reqTime)
				}
			}

			if len(validRequests) == 0 {
				delete(l.requests, userID)
			} else {
				l.requests[userID] = validRequests
			}
		}
		l.mu.Unlock()
	}
}

// Global rate limiters for different operations
var (
	// 100 requests per minute per user
	generalLimiter = NewUserRateLimiter(100, 1*time.Minute)

	// 10 exports per minute per user (Increased for Dev environment flexibility)
	exportLimiter = NewUserRateLimiter(25, 1*time.Minute)

	// 5 bulk operations per minute per user
	bulkLimiter = NewUserRateLimiter(5, 1*time.Minute)
)

// UserRateLimitMiddleware creates a middleware for per-user rate limiting
func UserRateLimitMiddleware(limiter *UserRateLimiter, message string) fiber.Handler {
	return func(c *fiber.Ctx) error {
		userID, ok := c.Locals("user_id").(uint)
		if !ok {
			// If no user ID, skip rate limiting (shouldn't happen for protected routes)
			return c.Next()
		}

		if !limiter.Allow(userID) {
			return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{
				"success": false,
				"message": message,
			})
		}

		return c.Next()
	}
}

// GeneralRateLimitMiddleware applies general rate limiting
func GeneralRateLimitMiddleware() fiber.Handler {
	return UserRateLimitMiddleware(generalLimiter, "Too many requests. Please slow down.")
}

// ExportRateLimitMiddleware applies rate limiting for export operations
func ExportRateLimitMiddleware() fiber.Handler {
	return UserRateLimitMiddleware(exportLimiter, "Too many export requests. Please wait before trying again.")
}

// BulkOperationRateLimitMiddleware applies rate limiting for bulk operations
func BulkOperationRateLimitMiddleware() fiber.Handler {
	return UserRateLimitMiddleware(bulkLimiter, "Too many bulk operations. Please wait before trying again.")
}
