package fosite

import (
	"net/http"
	"strings"

	"context"

	"github.com/pkg/errors"
)

func (c *Fosite) NewAuthorizeRequest(ctx context.Context, r *http.Request) (AuthorizeRequester, error) {
	request := &AuthorizeRequest{
		ResponseTypes:        Arguments{},
		HandledResponseTypes: Arguments{},
		Request:              *NewRequest(),
	}

	if err := r.ParseForm(); err != nil {
		return request, errors.Wrap(ErrInvalidRequest, err.Error())
	}

	request.Form = r.Form
	client, err := c.Store.GetClient(ctx, request.GetRequestForm().Get("client_id"))
	if err != nil {
		return request, errors.WithStack(ErrInvalidClient)
	}
	request.Client = client

	// Fetch redirect URI from request
	rawRedirURI, err := GetRedirectURIFromRequestValues(r.Form)
	if err != nil {
		return request, errors.Wrap(ErrInvalidRequest, err.Error())
	}

	// Validate redirect uri
	redirectURI, err := MatchRedirectURIWithClientRedirectURIs(rawRedirURI, client)
	if err != nil {
		return request, errors.Wrap(ErrInvalidRequest, err.Error())
	} else if !IsValidRedirectURI(redirectURI) {
		return request, errors.Wrap(ErrInvalidRequest, "not a valid redirect uri")
	}
	request.RedirectURI = redirectURI

	// https://tools.ietf.org/html/rfc6749#section-3.1.1
	// Extension response types MAY contain a space-delimited (%x20) list of
	// values, where the order of values does not matter (e.g., response
	// type "a b" is the same as "b a").  The meaning of such composite
	// response types is defined by their respective specifications.
	request.ResponseTypes = removeEmpty(strings.Split(r.Form.Get("response_type"), " "))

	// rfc6819 4.4.1.8.  Threat: CSRF Attack against redirect-uri
	// The "state" parameter should be used to link the authorization
	// request with the redirect URI used to deliver the access token (Section 5.3.5).
	//
	// https://tools.ietf.org/html/rfc6819#section-4.4.1.8
	// The "state" parameter should not	be guessable
	state := r.Form.Get("state")
	if len(state) < MinParameterEntropy {
		// We're assuming that using less then 8 characters for the state can not be considered "unguessable"
		return request, errors.Wrapf(ErrInvalidState, "state length must at least be %d characters long", MinParameterEntropy)
	}
	request.State = state

	// Remove empty items from arrays
	request.SetRequestedScopes(removeEmpty(strings.Split(r.Form.Get("scope"), " ")))
	return request, nil
}
