// Package server provides HTTP middleware functions for request processing.
package server

import (
	"context"
	"net/http"
	"runtime"
	"time"

	"git.dcentral.systems/toolz/goplt/pkg/errorbus"
	"git.dcentral.systems/toolz/goplt/pkg/logger"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type contextKey string

const (
	requestIDKey contextKey = "request_id"
	userIDKey    contextKey = "user_id"
)

// RequestIDMiddleware generates a unique request ID for each request.
func RequestIDMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		requestID := c.GetHeader("X-Request-ID")
		if requestID == "" {
			requestID = uuid.New().String()
		}

		c.Set(string(requestIDKey), requestID)
		c.Header("X-Request-ID", requestID)
		c.Next()
	}
}

// LoggingMiddleware logs all HTTP requests with structured logging.
func LoggingMiddleware(log logger.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		path := c.Request.URL.Path
		method := c.Request.Method

		// Process request
		c.Next()

		// Calculate duration
		duration := time.Since(start)

		// Get request ID from context
		requestID, _ := c.Get(string(requestIDKey))
		requestIDStr := ""
		if id, ok := requestID.(string); ok {
			requestIDStr = id
		}

		// Log request
		log.Info("HTTP request",
			logger.String("method", method),
			logger.String("path", path),
			logger.Int("status", c.Writer.Status()),
			logger.Any("duration_ms", duration.Milliseconds()),
			logger.String("request_id", requestIDStr),
			logger.String("ip", c.ClientIP()),
		)
	}
}

// PanicRecoveryMiddleware recovers from panics and publishes them to the error bus.
func PanicRecoveryMiddleware(errorBus errorbus.ErrorPublisher) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				// Capture stack trace
				stack := make([]byte, 4096)
				n := runtime.Stack(stack, false)
				stack = stack[:n]

				// Get request ID from context
				requestID, _ := c.Get(string(requestIDKey))
				ctx := context.WithValue(context.Background(), requestIDKey, requestID)

				// Create error
				var panicErr error
				if e, ok := err.(error); ok {
					panicErr = e
				} else {
					panicErr = &panicError{value: err, stack: stack}
				}

				// Publish to error bus
				errorBus.Publish(ctx, panicErr)

				// Return error response
				c.JSON(http.StatusInternalServerError, gin.H{
					"error": "Internal server error",
				})

				c.Abort()
			}
		}()

		c.Next()
	}
}

// panicError wraps a panic value as an error.
type panicError struct {
	value interface{}
	stack []byte
}

func (e *panicError) Error() string {
	return "panic recovered"
}

// CORSMiddleware provides CORS support.
func CORSMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE, PATCH")

		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
			return
		}

		c.Next()
	}
}

// TimeoutMiddleware sets a request timeout.
func TimeoutMiddleware(timeout time.Duration) gin.HandlerFunc {
	return func(c *gin.Context) {
		ctx, cancel := context.WithTimeout(c.Request.Context(), timeout)
		defer cancel()

		c.Request = c.Request.WithContext(ctx)
		c.Next()
	}
}
