package evaluator

import (
	"net/url"

	"google.golang.org/protobuf/types/known/timestamppb"

	"github.com/pomerium/pomerium/internal/directory"
	"github.com/pomerium/pomerium/pkg/grpc/session"
	"github.com/pomerium/pomerium/pkg/grpc/user"
)

type (
	// Request is the request data used for the evaluator.
	Request struct {
		DataBrokerData DataBrokerData `json:"databroker_data"`
		HTTP           RequestHTTP    `json:"http"`
		Session        RequestSession `json:"session"`
		CustomPolicies []string
	}

	// RequestHTTP is the HTTP field in the request.
	RequestHTTP struct {
		Method            string            `json:"method"`
		URL               string            `json:"url"`
		Headers           map[string]string `json:"headers"`
		ClientCertificate string            `json:"client_certificate"`
	}

	// RequestSession is the session field in the request.
	RequestSession struct {
		ID string `json:"id"`
	}
)

type sessionOrServiceAccount interface {
	GetId() string
	GetExpiresAt() *timestamppb.Timestamp
	GetIssuedAt() *timestamppb.Timestamp
	GetUserId() string
	GetImpersonateEmail() string
	GetImpersonateGroups() []string
	GetImpersonateUserId() string
}

func (req *Request) fillJWTPayload(payload map[string]interface{}) {
	if u, err := url.Parse(req.HTTP.URL); err == nil {
		payload["aud"] = u.Hostname()
	}

	if s, ok := req.DataBrokerData.Get("type.googleapis.com/session.Session", req.Session.ID).(*session.Session); ok {
		req.fillJWTPayloadSessionOrServiceAccount(payload, s)
	}

	if sa, ok := req.DataBrokerData.Get("type.googleapis.com/user.ServiceAccount", req.Session.ID).(*user.ServiceAccount); ok {
		req.fillJWTPayloadSessionOrServiceAccount(payload, sa)
	}
}

func (req *Request) fillJWTPayloadSessionOrServiceAccount(payload map[string]interface{}, s sessionOrServiceAccount) {
	payload["jti"] = s.GetId()
	if s.GetExpiresAt().IsValid() {
		payload["exp"] = s.GetExpiresAt().AsTime().Unix()
	}
	if s.GetIssuedAt().IsValid() {
		payload["iat"] = s.GetIssuedAt().AsTime().Unix()
	}

	userID := s.GetUserId()
	if s.GetImpersonateUserId() != "" {
		userID = s.GetImpersonateUserId()
	}
	if u, ok := req.DataBrokerData.Get("type.googleapis.com/user.User", userID).(*user.User); ok {
		payload["sub"] = u.GetId()
		payload["user"] = u.GetId()
		payload["email"] = u.GetEmail()
	}
	if du, ok := req.DataBrokerData.Get("type.googleapis.com/directory.User", userID).(*directory.User); ok {
		if du.GetEmail() != "" {
			payload["email"] = du.GetEmail()
		}
		var groupNames []string
		for _, groupID := range du.GetGroupIds() {
			if dg, ok := req.DataBrokerData.Get("type.googleapis.com/directory.Group", groupID).(*directory.Group); ok {
				groupNames = append(groupNames, dg.Name)
			}
		}
		var groups []string
		groups = append(groups, du.GetGroupIds()...)
		groups = append(groups, groupNames...)
		payload["groups"] = groups
	}

	if s.GetImpersonateEmail() != "" {
		payload["email"] = s.GetImpersonateEmail()
	}
	if len(s.GetImpersonateGroups()) > 0 {
		payload["groups"] = s.GetImpersonateGroups()
	}
}