package federatedloginv1

import (
	"context"
	"strings"
	"unicode"
	"unicode/utf8"
)

func ValidateBeginRequest(ctx context.Context, request BeginRequest) error {
	if err := validateContext(ctx); err != nil {
		return err
	}
	if err := validateInstallation(request.AppInstallationID); err != nil {
		return err
	}
	if !required(request.RedirectURI, MaxURIBytes) {
		return invalid(InvalidComponentFieldValue)
	}
	return nil
}

func ValidateBeginResponse(response BeginResponse) error {
	if !validState(response.State) || response.ExpiresAtUnixSecond <= 0 {
		return &Failure{Category: FailureInternal}
	}
	return nil
}

func ValidateConsumeRequest(ctx context.Context, request ConsumeRequest) error {
	if err := validateContext(ctx); err != nil {
		return err
	}
	if err := validateInstallation(request.AppInstallationID); err != nil {
		return err
	}
	if !validState(request.State) || !required(request.RedirectURI, MaxURIBytes) {
		return invalid(InvalidComponentFieldValue)
	}
	return nil
}

func ValidateConsumeResponse(ConsumeResponse) error { return nil }

func validateContext(ctx context.Context) error {
	if ctx == nil {
		return invalid(InvalidComponentDeadline)
	}
	if err := ctx.Err(); err != nil {
		if err == context.DeadlineExceeded {
			return &Failure{Category: FailureDeadlineExceeded}
		}
		return &Failure{Category: FailureCanceled}
	}
	if _, ok := ctx.Deadline(); !ok {
		return invalid(InvalidComponentDeadline)
	}
	return nil
}

func validateInstallation(value string) error {
	if required(value, MaxIdentifierBytes) {
		return nil
	}
	if len(value) > MaxIdentifierBytes {
		return &Failure{Category: FailureResourceExhausted, LimitKind: LimitAppInstallationID, Maximum: MaxIdentifierBytes}
	}
	return invalid(InvalidComponentAppInstallationID)
}

func required(value string, maximum int) bool {
	if value == "" || len(value) > maximum || !utf8.ValidString(value) || strings.TrimSpace(value) != value {
		return false
	}
	for _, character := range value {
		if unicode.IsControl(character) {
			return false
		}
	}
	return true
}

func invalid(component InvalidComponent) *Failure {
	return &Failure{Category: FailureInvalidRequest, InvalidComponent: component}
}
