package middleware

import (
	"context"
	"net/http"
	"strings"

	"go.b0esche.cloud/backend/internal/audit"
	"go.b0esche.cloud/backend/internal/database"
	"go.b0esche.cloud/backend/internal/org"
	"go.b0esche.cloud/backend/internal/permission"
	"go.b0esche.cloud/backend/pkg/jwt"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/google/uuid"
)

var RequestID = middleware.RequestID
var Logger = middleware.Logger
var Recoverer = middleware.Recoverer

// TODO: Implement rate limiter
var RateLimit = func(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Basic rate limiting logic here
		next.ServeHTTP(w, r)
	})
}

type contextKey string

const (
	userKey    contextKey = "user"
	sessionKey contextKey = "session"
	orgKey     contextKey = "org"
)

// Auth middleware
func Auth(jwtManager *jwt.Manager, db *database.DB) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			authHeader := r.Header.Get("Authorization")
			if !strings.HasPrefix(authHeader, "Bearer ") {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}

			tokenString := strings.TrimPrefix(authHeader, "Bearer ")
			claims, session, err := jwtManager.ValidateWithSession(r.Context(), tokenString, db)
			if err != nil {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}

			ctx := context.WithValue(r.Context(), userKey, claims.UserID)
			ctx = context.WithValue(ctx, sessionKey, session)
			next.ServeHTTP(w, r.WithContext(ctx))
		})
	}
}

// Org middleware
func Org(db *database.DB, auditLogger *audit.Logger) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			userIDStr := r.Context().Value(userKey).(string)
			userID, _ := uuid.Parse(userIDStr)

			orgIDStr := r.Header.Get("X-Org-ID")
			if orgIDStr == "" {
				orgIDStr = chi.URLParam(r, "orgId")
			}
			orgID, err := uuid.Parse(orgIDStr)
			if err != nil {
				http.Error(w, "Invalid org ID", http.StatusBadRequest)
				return
			}

			_, err = org.CheckMembership(r.Context(), db, userID, orgID)
			if err != nil {
				auditLogger.Log(r.Context(), audit.Entry{
					UserID:   &userID,
					Action:   "org_access",
					Success:  false,
					Metadata: map[string]interface{}{"org_id": orgID, "error": err.Error()},
				})
				http.Error(w, "Forbidden", http.StatusForbidden)
				return
			}

			ctx := context.WithValue(r.Context(), orgKey, orgID)
			next.ServeHTTP(w, r.WithContext(ctx))
		})
	}
}

// Permission middleware
func Permission(db *database.DB, auditLogger *audit.Logger, perm permission.Permission) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			userIDStr := r.Context().Value(userKey).(string)
			userID, _ := uuid.Parse(userIDStr)
			orgID := r.Context().Value(orgKey).(uuid.UUID)

			hasPerm, err := permission.HasPermission(r.Context(), db, userID, orgID, perm)
			if err != nil || !hasPerm {
				auditLogger.Log(r.Context(), audit.Entry{
					UserID:   &userID,
					OrgID:    &orgID,
					Action:   "permission_check",
					Resource: &[]string{string(perm)}[0],
					Success:  false,
					Metadata: map[string]interface{}{"permission": perm},
				})
				http.Error(w, "Forbidden", http.StatusForbidden)
				return
			}

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