package middleware

import (
	"fmt"
	"net/http"

	"go.uber.org/zap"

	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/config"
)

// RequestSizeLimitMiddleware enforces maximum request body size limits
// CWE-770: Prevents resource exhaustion through oversized requests
type RequestSizeLimitMiddleware struct {
	defaultMaxSize int64 // Default max request size in bytes
	logger         *zap.Logger
}

// NewRequestSizeLimitMiddleware creates a new request size limit middleware
func NewRequestSizeLimitMiddleware(cfg *config.Config, logger *zap.Logger) *RequestSizeLimitMiddleware {
	// Default to 10MB if not configured
	defaultMaxSize := int64(10 * 1024 * 1024) // 10 MB

	if cfg.HTTP.MaxRequestBodySize > 0 {
		defaultMaxSize = cfg.HTTP.MaxRequestBodySize
	}

	return &RequestSizeLimitMiddleware{
		defaultMaxSize: defaultMaxSize,
		logger:         logger.Named("request-size-limit-middleware"),
	}
}

// Limit returns a middleware that enforces request size limits
// CWE-770: Resource allocation without limits or throttling prevention
func (m *RequestSizeLimitMiddleware) Limit(maxSize int64) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// Use provided maxSize, or default if 0
			limit := maxSize
			if limit == 0 {
				limit = m.defaultMaxSize
			}

			// Set MaxBytesReader to limit request body size
			// This prevents clients from sending arbitrarily large requests
			r.Body = http.MaxBytesReader(w, r.Body, limit)

			// Call next handler
			next.ServeHTTP(w, r)
		})
	}
}

// LimitDefault returns a middleware that uses the default size limit
func (m *RequestSizeLimitMiddleware) LimitDefault() func(http.Handler) http.Handler {
	return m.Limit(0) // 0 means use default
}

// LimitSmall returns a middleware for small requests (1 MB)
// Suitable for: login, registration, simple queries
func (m *RequestSizeLimitMiddleware) LimitSmall() func(http.Handler) http.Handler {
	return m.Limit(1 * 1024 * 1024) // 1 MB
}

// LimitMedium returns a middleware for medium requests (5 MB)
// Suitable for: form submissions with some data
func (m *RequestSizeLimitMiddleware) LimitMedium() func(http.Handler) http.Handler {
	return m.Limit(5 * 1024 * 1024) // 5 MB
}

// LimitLarge returns a middleware for large requests (50 MB)
// Suitable for: file uploads, bulk operations
func (m *RequestSizeLimitMiddleware) LimitLarge() func(http.Handler) http.Handler {
	return m.Limit(50 * 1024 * 1024) // 50 MB
}

// ErrorHandler returns a middleware that handles MaxBytesReader errors gracefully
func (m *RequestSizeLimitMiddleware) ErrorHandler() func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			next.ServeHTTP(w, r)

			// Check if there was a MaxBytesReader error
			// This happens when the client sends more data than allowed
			if r.Body != nil {
				// Try to read one more byte to trigger the error
				buf := make([]byte, 1)
				_, err := r.Body.Read(buf)
				if err != nil && err.Error() == "http: request body too large" {
					m.logger.Warn("request body too large",
						zap.String("method", r.Method),
						zap.String("path", r.URL.Path),
						zap.String("remote_addr", r.RemoteAddr))

					http.Error(w, "Request body too large", http.StatusRequestEntityTooLarge)
					return
				}
			}
		})
	}
}

// Handler wraps an http.Handler with size limit and error handling
func (m *RequestSizeLimitMiddleware) Handler(maxSize int64) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return m.Limit(maxSize)(m.ErrorHandler()(next))
	}
}

// formatBytes formats bytes into human-readable format
func formatBytes(bytes int64) string {
	const unit = 1024
	if bytes < unit {
		return fmt.Sprintf("%d B", bytes)
	}
	div, exp := int64(unit), 0
	for n := bytes / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}
