package middleware

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/explorer/backend/auth"
	"github.com/explorer/backend/featureflags"
)

// AuthMiddleware handles authentication and authorization
type AuthMiddleware struct {
	walletAuth *auth.WalletAuth
}

// NewAuthMiddleware creates a new auth middleware
func NewAuthMiddleware(walletAuth *auth.WalletAuth) *AuthMiddleware {
	return &AuthMiddleware{
		walletAuth: walletAuth,
	}
}

// RequireAuth is middleware that requires authentication
func (m *AuthMiddleware) RequireAuth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		address, track, err := m.extractAuth(r)
		if err != nil {
			writeUnauthorized(w)
			return
		}

		ctx := ContextWithAuth(r.Context(), address, track, true)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

// RequireTrack is middleware that requires a specific track level
func (m *AuthMiddleware) RequireTrack(requiredTrack int) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			track := UserTrack(r.Context())

			if !featureflags.HasAccess(track, requiredTrack) {
				writeForbidden(w, requiredTrack)
				return
			}

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

// OptionalAuth is middleware that optionally authenticates (for Track 1 endpoints)
func (m *AuthMiddleware) OptionalAuth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		address, track, err := m.extractAuth(r)
		if err != nil {
			// No auth provided (or auth failed) — fall back to Track 1.
			ctx := ContextWithAuth(r.Context(), "", defaultTrackLevel, false)
			next.ServeHTTP(w, r.WithContext(ctx))
			return
		}

		ctx := ContextWithAuth(r.Context(), address, track, true)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

// extractAuth extracts authentication information from the request.
// Returns ErrMissingAuthorization when no usable Bearer token is present;
// otherwise returns the error from JWT validation.
func (m *AuthMiddleware) extractAuth(r *http.Request) (string, int, error) {
	authHeader := r.Header.Get("Authorization")
	if authHeader == "" {
		return "", 0, ErrMissingAuthorization
	}

	parts := strings.Split(authHeader, " ")
	if len(parts) != 2 || parts[0] != "Bearer" {
		return "", 0, ErrMissingAuthorization
	}

	token := parts[1]

	address, track, err := m.walletAuth.ValidateJWT(token)
	if err != nil {
		return "", 0, err
	}

	return address, track, nil
}

// writeUnauthorized writes a 401 Unauthorized response
func writeUnauthorized(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusUnauthorized)
	w.Write([]byte(`{"error":{"code":"unauthorized","message":"Authentication required"}}`))
}

// writeForbidden writes a 403 Forbidden response
func writeForbidden(w http.ResponseWriter, requiredTrack int) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusForbidden)
	w.Write([]byte(`{"error":{"code":"forbidden","message":"Insufficient permissions","required_track":` + fmt.Sprintf("%d", requiredTrack) + `}}`))
}

