package middlewares

import (
	"fmt"
	"net/http"
	"strings"
	"time"

	"gauth-central/internal/services"
	"gauth-central/pkg/utils"

	"github.com/gin-gonic/gin"
)

// RateLimitMiddleware creates a rate limiting middleware
func RateLimitMiddleware(maxRequests int64, duration time.Duration) gin.HandlerFunc {
	cacheService := services.NewCacheService()
	settingsService := services.NewSettingsService()

	return func(c *gin.Context) {
		// Get client IP
		clientIP := c.ClientIP()
		path := c.Request.URL.Path
		method := c.Request.Method

		// Skip checks for localhost (hardcoded safety)
		if clientIP == "::1" || clientIP == "127.0.0.1" || clientIP == "localhost" {
			fmt.Printf("%s[LOCALHOST BYPASS]%s IP: %s accessed %s %s\n", utils.ColorCyan, utils.ColorReset, clientIP, method, path)
			c.Next()
			return
		}

		// 1. Check Blacklist from DB
		blacklist, err := settingsService.GetActiveBlacklistOrigins()
		if err == nil {
			for _, blocked := range blacklist {
				if blocked == clientIP || strings.Contains(blocked, clientIP) {
					fmt.Printf("%s[BLACKLIST BLOCKED]%s IP: %s tried to access %s %s\n", utils.ColorRed, utils.ColorReset, clientIP, method, path)
					c.JSON(http.StatusForbidden, gin.H{
						"error": "Access denied. Your IP is blacklisted.",
					})
					c.Abort()
					return
				}
			}
		}

		// 2. Check Whitelist from DB (Skip Rate Limit)
		whitelist, err := settingsService.GetActiveWhitelistOrigins()
		if err == nil {
			for _, allowed := range whitelist {
				if allowed == clientIP || strings.Contains(allowed, clientIP) {
					fmt.Printf("%s[WHITELIST ALLOWED]%s IP: %s accessed %s %s (Rate Limit Skipped)\n", utils.ColorGreen, utils.ColorReset, clientIP, method, path)
					c.Next()
					return
				}
			}
		}

		key := clientIP

		// Increment counter
		count, err := cacheService.IncrementRateLimit(key, duration)
		if err != nil {
			// If Redis is down, allow the request but log error
			fmt.Printf("%s[REDIS ERROR]%s Could not increment rate limit for %s: %v\n", utils.ColorRed, utils.ColorReset, clientIP, err)
			c.Next()
			return
		}

		remaining := maxRequests - count
		if remaining < 0 {
			remaining = 0
		}

		// Check if limit exceeded
		if count > maxRequests {
			fmt.Printf("%s[RATE LIMIT EXCEEDED]%s IP: %s - %s %s - Limit: %d\n", utils.ColorYellow, utils.ColorReset, clientIP, method, path, maxRequests)
			c.JSON(http.StatusTooManyRequests, gin.H{
				"error": "Too many requests. Please try again later.",
			})
			c.Abort()
			return
		}

		// Log normal access with remaining limit
		fmt.Printf("[Rate Limit] IP: %s - %s %s - Used: %d/%d - Remaining: %d\n", clientIP, method, path, count, maxRequests, remaining)

		c.Next()
	}
}

// DynamicRateLimitMiddleware - Database'den ayarları okuyan rate limit middleware
func DynamicRateLimitMiddleware(settingName string, settingsService *services.SettingsService) gin.HandlerFunc {
	cacheService := services.NewCacheService()

	return func(c *gin.Context) {
		// Get client IP
		clientIP := c.ClientIP()
		path := c.Request.URL.Path
		method := c.Request.Method

		// Skip checks for localhost
		if clientIP == "::1" || clientIP == "127.0.0.1" || clientIP == "localhost" {
			fmt.Printf("%s[LOCALHOST BYPASS]%s IP: %s accessed %s %s\n", utils.ColorCyan, utils.ColorReset, clientIP, method, path)
			c.Next()
			return
		}

		// 1. Check Blacklist from DB
		blacklist, err := settingsService.GetActiveBlacklistOrigins()
		if err == nil {
			for _, blocked := range blacklist {
				if blocked == clientIP || strings.Contains(blocked, clientIP) {
					fmt.Printf("%s[BLACKLIST BLOCKED]%s IP: %s tried to access %s %s\n", utils.ColorRed, utils.ColorReset, clientIP, method, path)
					c.JSON(http.StatusForbidden, gin.H{
						"error": "Access denied. Your IP is blacklisted.",
					})
					c.Abort()
					return
				}
			}
		}

		// 2. Check Whitelist from DB (Skip Rate Limit)
		whitelist, err := settingsService.GetActiveWhitelistOrigins()
		if err == nil {
			for _, allowed := range whitelist {
				if allowed == clientIP || strings.Contains(allowed, clientIP) {
					fmt.Printf("%s[WHITELIST ALLOWED]%s IP: %s accessed %s %s (Rate Limit Skipped)\n", utils.ColorGreen, utils.ColorReset, clientIP, method, path)
					c.Next()
					return
				}
			}
		}

		// Get rate limit settings from database/cache
		setting, err := settingsService.GetRateLimitSettingByName(settingName)
		if err != nil || setting == nil {
			// If error or not found, use default and allow
			c.Next()
			return
		}

		// Check if setting is active
		if !setting.IsActive {
			c.Next()
			return
		}

		key := settingName + ":" + clientIP

		// Increment counter
		duration := time.Duration(setting.WindowSeconds) * time.Second
		count, err := cacheService.IncrementRateLimit(key, duration)
		if err != nil {
			// If Redis is down, allow the request
			c.Next()
			return
		}

		remaining := setting.MaxRequests - count
		if remaining < 0 {
			remaining = 0
		}

		// Check if limit exceeded
		if count > setting.MaxRequests {
			fmt.Printf("%s[RATE LIMIT EXCEEDED]%s IP: %s - %s %s - Limit: %d\n", utils.ColorYellow, utils.ColorReset, clientIP, method, path, setting.MaxRequests)
			c.JSON(http.StatusTooManyRequests, gin.H{
				"error":       "Too many requests. Please try again later.",
				"limit":       setting.MaxRequests,
				"window":      setting.WindowSeconds,
				"retry_after": setting.WindowSeconds,
			})
			c.Abort()
			return
		}

		// Log normal access with remaining limit
		fmt.Printf("[Rate Limit] IP: %s - %s %s - Used: %d/%d - Remaining: %d\n", clientIP, method, path, count, setting.MaxRequests, remaining)

		c.Next()
	}
}

// LoginRateLimitMiddleware limits login attempts per IP
func LoginRateLimitMiddleware() gin.HandlerFunc {
	return RateLimitMiddleware(5, 1*time.Minute) // 5 login attempts per minute
}

// RegisterRateLimitMiddleware limits registration attempts per IP
func RegisterRateLimitMiddleware() gin.HandlerFunc {
	return RateLimitMiddleware(3, 5*time.Minute) // 3 registration attempts per 5 minutes
}

// APIRateLimitMiddleware general API rate limiting
func APIRateLimitMiddleware() gin.HandlerFunc {
	return RateLimitMiddleware(100, 1*time.Minute) // 100 requests per minute
}
