package middleware

import (
	"log/slog"
	"net/http"
	"time"

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

// RequestLoggerConfig holds the configuration for the request logger.
type RequestLoggerConfig struct {
	SkipPaths []string
}

// DefaultRequestLoggerConfig returns the default request logger configuration.
func DefaultRequestLoggerConfig() *RequestLoggerConfig {
	return &RequestLoggerConfig{
		SkipPaths: []string{"/health", "/ready", "/metrics"},
	}
}

// Logger returns a Gin middleware handler for logging requests.
func Logger() gin.HandlerFunc {
	return LoggerWithConfig(DefaultRequestLoggerConfig())
}

// LoggerWithConfig returns a Gin middleware handler for structured
// request logging via slog. When LOG_FORMAT=json the output is
// machine-parseable JSON lines; otherwise human-readable key=value.
func LoggerWithConfig(config *RequestLoggerConfig) gin.HandlerFunc {
	skip := make(map[string]bool, len(config.SkipPaths))
	for _, p := range config.SkipPaths {
		skip[p] = true
	}

	return func(c *gin.Context) {
		path := c.Request.URL.Path
		if skip[path] {
			c.Next()
			return
		}

		start := time.Now()
		c.Next()

		reqID, _ := c.Get(RequestIDKey)

		attrs := []any{
			"method", c.Request.Method,
			"path", path,
			"status", c.Writer.Status(),
			"latency_ms", time.Since(start).Milliseconds(),
			"client_ip", c.ClientIP(),
		}
		if id, ok := reqID.(string); ok && id != "" {
			attrs = append(attrs, "request_id", id)
		}
		if uid := c.GetString("user_id"); uid != "" {
			attrs = append(attrs, "user_id", uid)
		}
		if errs := c.Errors.ByType(gin.ErrorTypePrivate).String(); errs != "" {
			attrs = append(attrs, "errors", errs)
		}

		slog.Info("request", attrs...)
	}
}

// Recovery returns a Gin middleware handler for recovering from panics.
func Recovery() gin.HandlerFunc {
	return RecoveryWithConfig(DefaultRecoveryConfig())
}

// RecoveryConfig holds the configuration for the recovery middleware.
type RecoveryConfig struct {
	SkipStackTrace bool
	StackTraceSize int
	StackAll       bool
}

// DefaultRecoveryConfig returns the default recovery configuration.
func DefaultRecoveryConfig() *RecoveryConfig {
	return &RecoveryConfig{
		SkipStackTrace: false,
		StackTraceSize: 1024,
		StackAll:       false,
	}
}

// RecoveryWithConfig returns a Gin middleware handler for recovering
// from panics with custom configuration.
func RecoveryWithConfig(config *RecoveryConfig) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				reqID, _ := c.Get(RequestIDKey)
				slog.Error("panic",
					"error", err,
					"method", c.Request.Method,
					"path", c.Request.URL.Path,
					"request_id", reqID,
				)
				c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
					"error": "internal server error",
				})
			}
		}()

		c.Next()
	}
}
