package api

import (
	"bufio"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"time"
)

// requestLoggingMiddleware логирует каждый HTTP-запрос с Request ID, методом, путём, статусом и длительностью.
func requestLoggingMiddleware(logFormat string) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// Не логируем health-check запросы и OPTIONS
			if r.URL.Path == "/api/health/live" || r.URL.Path == "/api/health/ready" || r.Method == "OPTIONS" {
				next.ServeHTTP(w, r)
				return
			}

			reqID := generateRequestID()
			r.Header.Set("X-Request-ID", reqID)
			w.Header().Set("X-Request-ID", reqID)

			start := time.Now()
			lrw := &loggingResponseWriter{ResponseWriter: w, statusCode: http.StatusOK}

			next.ServeHTTP(lrw, r)

			duration := time.Since(start)

			// Извлекаем пользователя из контекста (может быть nil)
			userStr := "-"
			if user := GetUserFromContext(r.Context()); user != nil {
				userStr = user.Username
			}

			switch logFormat {
			case "json":
				entry := map[string]interface{}{
					"ts":          start.UTC().Format(time.RFC3339),
					"id":          reqID,
					"method":      r.Method,
					"path":        r.URL.Path,
					"status":      lrw.statusCode,
					"duration_ms": float64(duration.Microseconds()) / 1000.0,
					"remote_addr": r.RemoteAddr,
					"user":        userStr,
				}
				data, _ := json.Marshal(entry)
				log.Printf("%s", string(data))
			default:
				log.Printf("[%s] %s %s %d %.2fms %s user=%s",
					reqID, r.Method, r.URL.Path, lrw.statusCode,
					float64(duration.Microseconds())/1000.0, r.RemoteAddr, userStr)
			}
		})
	}
}

// generateRequestID генерирует случайный Request ID: req-XXXXXXXX.
func generateRequestID() string {
	b := make([]byte, 6)
	if _, err := rand.Read(b); err != nil {
		return "req-" + hex.EncodeToString([]byte{0, 0, 0, 0, 0, 0})
	}
	return "req-" + hex.EncodeToString(b)
}

// loggingResponseWriter оборачивает http.ResponseWriter для перехвата статус-кода.
type loggingResponseWriter struct {
	http.ResponseWriter
	statusCode int
}

func (w *loggingResponseWriter) WriteHeader(code int) {
	w.statusCode = code
	w.ResponseWriter.WriteHeader(code)
}

func (w *loggingResponseWriter) Write(b []byte) (int, error) {
	return w.ResponseWriter.Write(b)
}

func (w *loggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	hijacker, ok := w.ResponseWriter.(http.Hijacker)
	if !ok {
		return nil, nil, fmt.Errorf("loggingResponseWriter: underlying ResponseWriter does not implement http.Hijacker")
	}
	return hijacker.Hijack()
}
