package handlers

import (
	"crypto/elliptic"
	"fmt"
	"net/url"

	"github.com/authelia/authelia/internal/authentication"
	"github.com/authelia/authelia/internal/middlewares"
	"github.com/tstranex/u2f"
)

// SecondFactorU2FSignPost handler for completing a signing request.
func SecondFactorU2FSignPost(ctx *middlewares.AutheliaCtx) {
	var requestBody signU2FRequestBody
	err := ctx.ParseBody(&requestBody)

	if err != nil {
		ctx.Error(err, mfaValidationFailedMessage)
		return
	}

	userSession := ctx.GetSession()
	if userSession.U2FChallenge == nil {
		ctx.Error(fmt.Errorf("U2F signing has not been initiated yet (no challenge)"), mfaValidationFailedMessage)
		return
	}

	if userSession.U2FRegistration == nil {
		ctx.Error(fmt.Errorf("U2F signing has not been initiated yet (no registration)"), mfaValidationFailedMessage)
		return
	}

	var registration u2f.Registration
	registration.KeyHandle = userSession.U2FRegistration.KeyHandle
	x, y := elliptic.Unmarshal(elliptic.P256(), userSession.U2FRegistration.PublicKey)
	registration.PubKey.Curve = elliptic.P256()
	registration.PubKey.X = x
	registration.PubKey.Y = y

	// TODO(c.michaud): store the counter to help detecting cloned U2F keys.
	_, err = registration.Authenticate(
		requestBody.SignResponse, *userSession.U2FChallenge, 0)

	if err != nil {
		ctx.Error(err, mfaValidationFailedMessage)
		return
	}

	userSession.AuthenticationLevel = authentication.TwoFactor
	err = ctx.SaveSession(userSession)

	if err != nil {
		ctx.Error(fmt.Errorf("Unable to update authentication level with U2F: %s", err), mfaValidationFailedMessage)
		return
	}

	if requestBody.TargetURL != "" {
		targetURL, err := url.ParseRequestURI(requestBody.TargetURL)

		if err != nil {
			ctx.Error(fmt.Errorf("Unable to parse target URL with U2F: %s", err), mfaValidationFailedMessage)
			return
		}

		if targetURL != nil && isRedirectionSafe(*targetURL, ctx.Configuration.Session.Domain) {
			ctx.SetJSONBody(redirectResponse{Redirect: requestBody.TargetURL})
		} else {
			ctx.ReplyOK()
		}
	} else {
		ctx.ReplyOK()
	}
}