package servers

import (
	"github.com/1f349/mjwt"
	"github.com/1f349/mjwt/auth"
	"github.com/1f349/orchid/database"
	"github.com/1f349/orchid/logger"
	vUtils "github.com/1f349/violet/utils"
	"github.com/julienschmidt/httprouter"
	"net/http"
)

type AuthClaims mjwt.BaseTypeClaims[auth.AccessTokenClaims]

type AuthCallback func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims)

type CertAuthCallback func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims, certId int64)

// checkAuth validates the bearer token against a mjwt.Verifier and returns an
// error message or continues to the next handler
func checkAuth(verify *mjwt.KeyStore, cb AuthCallback) httprouter.Handle {
	return func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
		// Get bearer token
		bearer := vUtils.GetBearer(req)
		if bearer == "" {
			apiError(rw, http.StatusForbidden, "Missing bearer token")
			return
		}

		// Read claims from mjwt
		_, b, err := mjwt.ExtractClaims[auth.AccessTokenClaims](verify, bearer)
		if err != nil {
			apiError(rw, http.StatusForbidden, "Invalid token")
			return
		}

		cb(rw, req, params, AuthClaims(b))
	}
}

// checkAuthWithPerm validates the bearer token and checks if it contains a
// required permission and returns an error message or continues to the next
// handler
func checkAuthWithPerm(verify *mjwt.KeyStore, perm string, cb AuthCallback) httprouter.Handle {
	return checkAuth(verify, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) {
		// check perms
		if !b.Claims.Perms.Has(perm) {
			apiError(rw, http.StatusForbidden, "No permission")
			return
		}
		cb(rw, req, params, b)
	})
}

// checkAuthForCertificate
func checkAuthForCertificate(verify *mjwt.KeyStore, perm string, db *database.Queries, cb CertAuthCallback) httprouter.Handle {
	return checkAuthWithPerm(verify, perm, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) {
		// lookup certificate owner
		id, err := checkCertOwner(db, params.ByName("id"), b)
		if err != nil {
			if err.Error() == "not the certificate owner" {
				apiError(rw, http.StatusBadRequest, "Not the certificate owner")
				return
			}
			apiError(rw, http.StatusInsufficientStorage, "Database error")
			logger.Logger.Info("[API] Failed to find certificate owner:", "err", err)
			return
		}

		cb(rw, req, params, b, id)
	})
}
