package logger

import (
	"bytes"
	"io"
	"strings"
	"time"

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

// RequestLoggerMiddleware creates a Gin middleware for request logging
func RequestLoggerMiddleware(logger *Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		// Generate request ID if not present
		requestID := c.GetHeader("X-Request-ID")
		if requestID == "" {
			requestID = uuid.New().String()
			c.Header("X-Request-ID", requestID)
		}

		// Get correlation ID
		correlationID := c.GetHeader("X-Correlation-ID")
		if correlationID == "" {
			correlationID = uuid.New().String()
			c.Header("X-Correlation-ID", correlationID)
		}

		// Create request-scoped logger
		reqLogger := logger.
			WithRequestID(requestID).
			WithCorrelationID(correlationID)

		// Store logger in context
		c.Set("logger", reqLogger)
		c.Set("request_id", requestID)
		c.Set("correlation_id", correlationID)

		// Capture request body for logging if needed
		var requestBody []byte
		if c.Request.Body != nil && strings.HasPrefix(c.ContentType(), "application/json") {
			requestBody, _ = io.ReadAll(c.Request.Body)
			c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
		}

		// Start timer
		start := time.Now()

		// Log request start
		reqLogger.Info("Request started", map[string]interface{}{
			"method":       c.Request.Method,
			"path":         c.Request.URL.Path,
			"query":        c.Request.URL.RawQuery,
			"remote_addr":  c.Request.RemoteAddr,
			"user_agent":   c.Request.UserAgent(),
			"content_type": c.ContentType(),
			"body_size":    len(requestBody),
		})

		// Process request
		c.Next()

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

		// Get response status
		status := c.Writer.Status()
		responseSize := c.Writer.Size()

		// Log level based on status code
		var logLevel LogLevel
		switch {
		case status >= 500:
			logLevel = ERROR
		case status >= 400:
			logLevel = WARN
		default:
			logLevel = INFO
		}

		// Log request completion
		fields := map[string]interface{}{
			"method":         c.Request.Method,
			"path":           c.Request.URL.Path,
			"status":         status,
			"duration":       duration.String(),
			"duration_ms":    duration.Milliseconds(),
			"response_size":  responseSize,
			"client_ip":      c.ClientIP(),
			"user_agent":     c.Request.UserAgent(),
			"content_type":   c.ContentType(),
			"content_length": c.Request.ContentLength,
		}

		// Add query parameters if present
		if c.Request.URL.RawQuery != "" {
			fields["query"] = c.Request.URL.RawQuery
		}

		// Add error information if present
		if len(c.Errors) > 0 {
			errors := make([]string, len(c.Errors))
			for i, err := range c.Errors {
				errors[i] = err.Error()
			}
			fields["errors"] = errors
		}

		reqLogger.log(logLevel, "Request completed", &duration, fields)
	}
}

// GetLoggerFromContext retrieves the logger from Gin context
func GetLoggerFromContext(c *gin.Context) *Logger {
	if logger, exists := c.Get("logger"); exists {
		if l, ok := logger.(*Logger); ok {
			return l
		}
	}
	return globalLogger
}

// GetRequestIDFromContext retrieves the request ID from Gin context
func GetRequestIDFromContext(c *gin.Context) string {
	if requestID, exists := c.Get("request_id"); exists {
		if id, ok := requestID.(string); ok {
			return id
		}
	}
	return ""
}

// GetCorrelationIDFromContext retrieves the correlation ID from Gin context
func GetCorrelationIDFromContext(c *gin.Context) string {
	if correlationID, exists := c.Get("correlation_id"); exists {
		if id, ok := correlationID.(string); ok {
			return id
		}
	}
	return ""
}

// DatabaseLoggerMiddleware creates middleware for database operation logging
func DatabaseLoggerMiddleware(logger *Logger, serviceName string) gin.HandlerFunc {
	return func(c *gin.Context) {
		reqLogger := GetLoggerFromContext(c).WithService(serviceName)
		c.Set("db_logger", reqLogger)
		c.Next()
	}
}

// GetDBLoggerFromContext retrieves the database logger from Gin context
func GetDBLoggerFromContext(c *gin.Context) *Logger {
	if logger, exists := c.Get("db_logger"); exists {
		if l, ok := logger.(*Logger); ok {
			return l
		}
	}
	return GetLoggerFromContext(c)
}

// ServiceLogger creates a service-specific logger
func ServiceLogger(serviceName string) *Logger {
	return globalLogger.WithService(serviceName)
}

// AuthServiceLogger returns a logger for auth service
func AuthServiceLogger() *Logger {
	return ServiceLogger("auth-service")
}

// BPJSServiceLogger returns a logger for BPJS service
func BPJSServiceLogger() *Logger {
	return ServiceLogger("bpjs-service")
}

// RetribusiServiceLogger returns a logger for retribusi service
func RetribusiServiceLogger() *Logger {
	return ServiceLogger("retribusi-service")
}

// DatabaseServiceLogger returns a logger for database operations
func DatabaseServiceLogger() *Logger {
	return ServiceLogger("database-service")
}

// MiddlewareServiceLogger returns a logger for middleware operations
func MiddlewareServiceLogger() *Logger {
	return ServiceLogger("middleware-service")
}
