package auth

import (
	"fmt"
	"net/http"
)

func New(settings Settings, debugLogger DebugLogger) (
	middleware func(http.Handler) http.Handler,
	err error,
) {
	routeToRoles, err := settingsToLookupMap(settings)
	if err != nil {
		return nil, fmt.Errorf("converting settings to lookup maps: %w", err)
	}

	return func(handler http.Handler) http.Handler {
		return &authHandler{
			childHandler: handler,
			routeToRoles: routeToRoles,
			logger:       debugLogger,
		}
	}, nil
}

type authHandler struct {
	childHandler http.Handler
	routeToRoles map[string][]internalRole
	logger       DebugLogger
}

func (h *authHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	route := request.Method + " " + request.URL.Path
	roles := h.routeToRoles[route]
	if len(roles) == 0 {
		h.logger.Debugf("no authentication role defined for route %s", route)
		http.Error(writer, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		return
	}

	responseHeader := make(http.Header, 0)
	for _, role := range roles {
		if !role.checker.isAuthorized(responseHeader, request) {
			continue
		}

		h.logger.Debugf("access to route %s authorized for role %s", route, role.name)
		h.childHandler.ServeHTTP(writer, request)
		return
	}

	// Flush out response headers if all roles failed to authenticate
	for headerKey, headerValues := range responseHeader {
		for _, headerValue := range headerValues {
			writer.Header().Add(headerKey, headerValue)
		}
	}

	allRoleNames := make([]string, len(roles))
	for i, role := range roles {
		allRoleNames[i] = role.name
	}
	h.logger.Debugf("access to route %s unauthorized after checking for roles %s",
		route, andStrings(allRoleNames))
	http.Error(writer, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
}
