package middleware

import (
	"strings"
	"time"

	"github.com/MicahParks/keyfunc/v3"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/warmbly/warmbly/internal/errx"
)

const (
	googleIssuer = "https://accounts.google.com"
)

type OidcHandler struct {
	ServiceAccount string
	KeySet         keyfunc.Keyfunc
	AppEnv         string
}

func (h *OidcHandler) Middleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		if h.AppEnv == "dev" {
			c.Next()
			return
		}

		// No key set means the GCP Cloud Tasks OIDC path isn't configured (the
		// default local dispatcher calls handlers in-process). Fail closed
		// rather than deref a nil key set if a webhook request slips through.
		if h.KeySet == nil {
			errx.Handle(c, errx.ErrForbidden)
			return
		}

		auth := c.GetHeader("Authorization")
		if auth == "" || !strings.HasPrefix(auth, "Bearer ") {
			errx.Handle(c, errx.ErrForbidden)
			return
		}

		tokenStr := strings.TrimPrefix(auth, "Bearer ")

		token, err := jwt.Parse(tokenStr, h.KeySet.Keyfunc, jwt.WithLeeway(10*time.Second))
		if err != nil {
			errx.Handle(c, errx.ErrForbidden)
			return
		}

		if !token.Valid {
			errx.Handle(c, errx.ErrForbidden)
			return
		}

		claims, ok := token.Claims.(jwt.MapClaims)
		if !ok {
			errx.Handle(c, errx.ErrForbidden)
			return
		}

		if iss, _ := claims.GetIssuer(); iss != googleIssuer {
			errx.Handle(c, errx.ErrForbidden)
			return
		}

		if sAccount, _ := claims.GetSubject(); sAccount != h.ServiceAccount {
			errx.Handle(c, errx.ErrForbidden)
			return
		}

		c.Next()
	}
}
