package middleware

import (
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/kernel/pkg/auth"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/rs/zerolog/log"
)

// RequestLogger returns a middleware that logs requests using zerolog
func RequestLogger() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		path := c.Request.URL.Path
		raw := c.Request.URL.RawQuery

		// Process request
		c.Next()

		// Log results
		latency := time.Since(start)
		clientIP := c.ClientIP()
		method := c.Request.Method
		statusCode := c.Writer.Status()
		errorMessage := c.Errors.ByType(gin.ErrorTypePrivate).String()

		// Get Request ID from context (set by RequestID middleware)
		reqID := c.GetString(ContextKeyRequestID)
		if reqID == "" {
			reqID = "-"
		}

		// Get Traceparent from context and extract trace ID (set by Tracing middleware)
		traceparent := c.GetString(string(constants.ContextKeyTraceparent))
		traceID := ""
		if traceparent != "" {
			// Extract trace ID from traceparent: 00-TRACEID-SPANID-01
			parts := strings.Split(traceparent, "-")
			if len(parts) >= 2 {
				traceID = parts[1]
			}
		}

		event := log.Info()
		if statusCode >= 500 {
			event = log.Error()
		} else if statusCode >= 400 {
			event = log.Warn()
		}

		if raw != "" {
			path = path + "?" + raw
		}

		event = event.
			Str("request_id", reqID).
			Str("trace_id", traceID).
			Int("status", statusCode).
			Str("method", method).
			Str("path", path).
			Str("ip", clientIP).
			Dur("latency", latency)

		if user, ok := authenticatedUser(c); ok {
			event = event.
				Bool("authenticated", true).
				Str("user_id", user.ID).
				Str("session_id", c.GetString(constants.ContextKeySessionID))
			if email := strings.TrimSpace(user.Email); email != "" {
				event = event.Str("email", email)
			}
		} else {
			event = event.Bool("authenticated", false)
		}

		event.Msg("HTTP Request")

		if errorMessage != "" {
			log.Error().
				Str("request_id", reqID).
				Str("trace_id", traceID).
				Str("error", errorMessage).
				Msg("Request Error")
		}
	}
}

func authenticatedUser(c *gin.Context) (auth.UserSession, bool) {
	userValue, exists := c.Get(constants.ContextKeyUser)
	if !exists {
		return auth.UserSession{}, false
	}
	user, ok := userValue.(auth.UserSession)
	if !ok || strings.TrimSpace(user.ID) == "" {
		return auth.UserSession{}, false
	}
	return user, true
}
