package middleware

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

	"warpbox.dev/backend/libs/services"
)

func Bans(logger *slog.Logger, bans *services.BanService, trustedProxies []string) Middleware {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ip, ok := services.ClientIPFromContext(r)
			if !ok {
				ip = services.ClientIP(r.RemoteAddr, r.Header.Get("X-Forwarded-For"), r.Header.Get("X-Real-IP"), trustedProxies)
				r = services.WithClientIP(r, ip)
			}
			now := time.Now().UTC()
			protectedProxy := services.IsProtectedProxyIP(ip, trustedProxies)

			if bans != nil && !protectedProxy {
				if matched, ok, err := bans.Match(ip, now); err != nil {
					logger.Error("ban match failed", "source", "ban", "severity", "error", "code", 5001, "ip", ip, "error", err.Error())
				} else if ok {
					logger.Warn("banned request blocked", "source", "ban", "severity", "warn", "code", 4030, "ip", ip, "ban_id", matched.Ban.ID, "target", matched.Ban.Normalized, "path", r.URL.Path)
					w.Header().Set("Content-Type", "text/plain; charset=utf-8")
					w.WriteHeader(http.StatusForbidden)
					_, _ = w.Write([]byte("forbidden\n"))
					return
				}

				settings, err := bans.Settings()
				if err != nil {
					logger.Error("ban settings load failed", "source", "ban", "severity", "error", "code", 5004, "ip", ip, "error", err.Error())
					next.ServeHTTP(w, r)
					return
				}
				if !settings.AutoBanEnabled {
					next.ServeHTTP(w, r)
					return
				}
				if pattern, err := bans.MaliciousPattern(r.URL.Path); err != nil {
					logger.Error("malicious path check failed", "source", "ban", "severity", "error", "code", 5002, "ip", ip, "error", err.Error())
				} else if pattern != "" {
					if result, err := bans.RecordAbuse(ip, services.AbuseKindMaliciousPath, r.URL.Path, settings.MaliciousPathThreshold, now); err != nil {
						logger.Error("malicious path event failed", "source", "ban", "severity", "error", "code", 5003, "ip", ip, "path", r.URL.Path, "error", err.Error())
					} else if result.Enabled {
						logger.Warn("malicious path requested", "source", "ban", "severity", "warn", "code", 4302, "ip", ip, "path", r.URL.Path, "pattern", pattern, "count", result.Event.Count)
						if result.Triggered {
							logger.Warn("ip auto-banned for malicious path", "source", "ban", "severity", "warn", "code", 4303, "ip", ip, "ban_id", result.Ban.ID, "path", r.URL.Path)
							w.Header().Set("Content-Type", "text/plain; charset=utf-8")
							w.WriteHeader(http.StatusForbidden)
							_, _ = w.Write([]byte("forbidden\n"))
							return
						}
					}
				}
			}

			next.ServeHTTP(w, r)
		})
	}
}
