package middleware

import (
	"net/http"
	"strings"

	"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
	"github.com/ultisuite/ulti-backend/internal/apitokens"
)

func EnforceApiTokenPolicy() func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			auth := ApiTokenFromContext(r.Context())
			if auth == nil {
				next.ServeHTTP(w, r)
				return
			}

			if strings.HasPrefix(r.URL.Path, "/api/v1/search") {
				reqs := apitokens.SearchRequirements(r.URL.Query().Get("types"))
				for _, req := range reqs {
					if !apitokens.AllowsRequirement(auth, req) {
						apiresponse.WriteError(w, r, http.StatusForbidden, apiresponse.CodeAuthForbidden, "insufficient api token permission", nil)
						return
					}
				}
				accountID := apitokens.ExtractMailAccountID(r.URL.Path, r.URL.Query().Get("account_id"))
				if accountID != "" && !apitokens.AllowsMailAccount(auth, accountID) {
					apiresponse.WriteError(w, r, http.StatusForbidden, apiresponse.CodeAuthForbidden, "mail account out of token scope", nil)
					return
				}
				next.ServeHTTP(w, r)
				return
			}

			req, ok := apitokens.RequirementForRequest(r.Method, r.URL.Path, r.URL.Query().Get("types"))
			if !ok {
				apiresponse.WriteError(w, r, http.StatusForbidden, apiresponse.CodeAuthForbidden, "route not allowed for api token", nil)
				return
			}
			if !apitokens.AllowsRequirement(auth, req) {
				apiresponse.WriteError(w, r, http.StatusForbidden, apiresponse.CodeAuthForbidden, "insufficient api token permission", nil)
				return
			}

			switch req.ScopeHint {
			case apitokens.ScopeMailAccountQuery, apitokens.ScopeMailAccountPath:
				accountID := apitokens.ExtractMailAccountID(r.URL.Path, r.URL.Query().Get("account_id"))
				if accountID != "" && !apitokens.AllowsMailAccount(auth, accountID) {
					apiresponse.WriteError(w, r, http.StatusForbidden, apiresponse.CodeAuthForbidden, "mail account out of token scope", nil)
					return
				}
			case apitokens.ScopeDrivePathFromURL:
				drivePath := apitokens.ExtractDrivePathFromURL(r.URL.Path)
				if drivePath != "" && !apitokens.AllowsDrivePath(auth, drivePath) {
					apiresponse.WriteError(w, r, http.StatusForbidden, apiresponse.CodeAuthForbidden, "drive path out of token scope", nil)
					return
				}
			}

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