package httpapi

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// WithRequestLogging wraps h with a handler that logs each request to dir/access.log.
// Uses dir "var/logs" by default. Returns h unchanged if dir is empty.
func WithRequestLogging(dir string, h http.Handler) (http.Handler, error) {
	if dir == "" {
		return h, nil
	}
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, err
	}
	f, err := os.OpenFile(filepath.Join(dir, "access.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return nil, err
	}
	return &requestLogger{handler: h, file: f}, nil
}

type requestLogger struct {
	handler http.Handler
	file    *os.File
	mu      sync.Mutex
}

func (l *requestLogger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	ip := clientIP(r)
	method := r.Method
	path := r.URL.Path
	userAgent := r.Header.Get("User-Agent")
	if userAgent == "" {
		userAgent = "-"
	}

	wrapped := &responseRecorder{ResponseWriter: w, status: http.StatusOK}
	l.handler.ServeHTTP(wrapped, r)

	elapsed := time.Since(start).Milliseconds()
	line := formatLogLine(ip, method, path, wrapped.status, elapsed, strings.ReplaceAll(userAgent, "\"", "'"))

	l.mu.Lock()
	_, _ = l.file.WriteString(line)
	l.mu.Unlock()
}

type responseRecorder struct {
	http.ResponseWriter
	status int
}

func (r *responseRecorder) WriteHeader(code int) {
	r.status = code
	r.ResponseWriter.WriteHeader(code)
}

func formatLogLine(ip, method, path string, status int, elapsedMs int64, userAgent string) string {
	// Common log format: ip - - [timestamp] "method path protocol" status size "referer" "user-agent" elapsed_ms
	t := time.Now().UTC().Format("02/Jan/2006:15:04:05 -0700")
	return fmt.Sprintf("%s - - [%s] \"%s %s\" %d %d \"-\" \"%s\" %dms\n",
		ip, t, method, path, status, 0, userAgent, elapsedMs)
}

func clientIP(r *http.Request) string {
	if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
		// First IP in the list is the original client
		for i := 0; i < len(xff); i++ {
			if xff[i] == ',' {
				return xff[:i]
			}
		}
		return xff
	}
	if xri := r.Header.Get("X-Real-IP"); xri != "" {
		return xri
	}
	host, _ := splitHostPort(r.RemoteAddr)
	return host
}

func splitHostPort(addr string) (host, port string) {
	if idx := strings.LastIndex(addr, ":"); idx >= 0 {
		return addr[:idx], addr[idx+1:]
	}
	return addr, ""
}
