package tlsauth

import (
	"context"

	sr "tildegit.org/tjp/sliderule"
	"tildegit.org/tjp/sliderule/gemini"
)

// GeminiAuth builds an authentication middleware from approval criteria.
//
// If a request does not contain a client certificate it will be rejected
// with a "60 certificate required" response. If the client identity does
// not pass the approver it will be rejected with "62 certificate invalid".
func GeminiAuth(approver Approver) sr.Middleware {
	return func(inner sr.Handler) sr.Handler {
		return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response {
			if Identity(request) == nil {
				return geminiMissingCert(ctx, request)
			}
			if !approver(ctx, request) {
				return geminiCertNotAuthorized(ctx, request)
			}

			return inner.Handle(ctx, request)
		})
	}
}

// GeminiOptionalAuth builds auth middleware which doesn't require an identity.
//
// If there is no client certificate the request will pass through the middleware.
// It will only be rejected with "62 certificate invalid" if there *is* a client
// certificate, but it fails the approval.
func GeminiOptionalAuth(approver Approver) sr.Middleware {
	return func(inner sr.Handler) sr.Handler {
		return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response {
			if Identity(request) != nil && !approver(ctx, request) {
				return geminiCertNotAuthorized(ctx, request)
			}

			return inner.Handle(ctx, request)
		})
	}
}

// GeminiRequireCertificate is a middleware that only requires a client certificate.
var GeminiRequireCertificate = GeminiAuth(Allow)

func geminiMissingCert(_ context.Context, _ *sr.Request) *sr.Response {
	return gemini.RequireCert("A client certificate is required.")
}

func geminiCertNotAuthorized(_ context.Context, _ *sr.Request) *sr.Response {
	return gemini.CertAuthFailure("Client certificate not authorized.")
}