package middleware

import (
	"net/http"
	"time"

	"xorm.io/xorm"

	"github.com/forgeo/forgebucket/internal/events"
	"github.com/forgeo/forgebucket/internal/models"
)

// statusRecorder wraps ResponseWriter to capture the written status code.
type statusRecorder struct {
	http.ResponseWriter
	status int
}

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

// AuditLog middleware records every state-mutating request (POST/PUT/PATCH/DELETE)
// to the audit_log table and publishes an audit.event to the event bus.
// It runs after auth middleware so the actor is available in context.
func AuditLog(db *xorm.Engine, bus events.EventBus) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			switch r.Method {
			case http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace:
				next.ServeHTTP(w, r)
				return
			}

			rec := &statusRecorder{ResponseWriter: w, status: http.StatusOK}
			next.ServeHTTP(rec, r)

			actorID, _ := UserIDFromContext(r.Context())
			actorName, _ := r.Context().Value(ContextKeyUsername).(string)

			entry := &models.AuditLog{
				ActorID:    actorID,
				ActorName:  actorName,
				Method:     r.Method,
				Path:       r.URL.Path,
				StatusCode: rec.status,
				IPAddress:  r.RemoteAddr,
				UserAgent:  r.UserAgent(),
				OccurredAt: time.Now().UTC(),
			}

			go func() {
				db.Insert(entry) //nolint:errcheck
				bus.Publish(events.SubjectAuditEvent, events.AuditEvent{ //nolint:errcheck
					ActorID:      entry.ActorID,
					ActorName:    entry.ActorName,
					Action:       entry.Method + " " + entry.Path,
					ResourcePath: entry.Path,
					IPAddress:    entry.IPAddress,
					StatusCode:   entry.StatusCode,
					At:           entry.OccurredAt,
				})
			}()
		})
	}
}
