package binding

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
)

const (
	idempotencyHeader       = "Idempotency-Key"
	maxIdempotencyKeyLength = 128
)

func bindingETag(bindingID string, revision int64) string {
	return fmt.Sprintf(`"workflow-binding:%s:%d"`, strings.TrimSpace(bindingID), revision)
}

func bindingRevisionETag(revisionID string, revision int64, digest string) string {
	return fmt.Sprintf(`"workflow-binding-revision:%s:%d:%s"`, strings.TrimSpace(revisionID), revision, strings.TrimSpace(digest))
}

func requireBindingRevision(raw string, bindingID string) (int64, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return 0, publicError(http.StatusPreconditionRequired, "precondition_required", "If-Match is required")
	}
	if strings.HasPrefix(raw, "W/") || !strings.HasPrefix(raw, `"`) || !strings.HasSuffix(raw, `"`) || strings.Contains(raw, ",") {
		return 0, publicError(http.StatusBadRequest, "binding_invalid_if_match", "If-Match must contain exactly one strong Binding ETag")
	}
	prefix := "workflow-binding:" + strings.TrimSpace(bindingID) + ":"
	value := strings.TrimSuffix(strings.TrimPrefix(raw, `"`), `"`)
	if !strings.HasPrefix(value, prefix) {
		return 0, publicError(http.StatusBadRequest, "binding_invalid_if_match", "If-Match is for the wrong Binding resource")
	}
	revision, err := strconv.ParseInt(strings.TrimPrefix(value, prefix), 10, 64)
	if err != nil || revision < 1 {
		return 0, publicError(http.StatusBadRequest, "binding_invalid_if_match", "If-Match contains an invalid Binding revision")
	}
	return revision, nil
}

func requireIdempotencyKey(raw string) (string, error) {
	key := strings.TrimSpace(raw)
	if key == "" {
		return "", publicError(http.StatusBadRequest, "binding_idempotency_key_required", "Idempotency-Key is required")
	}
	if len(key) > maxIdempotencyKeyLength {
		return "", publicError(http.StatusBadRequest, "binding_invalid_idempotency_key", fmt.Sprintf("Idempotency-Key must not exceed %d bytes", maxIdempotencyKeyLength))
	}
	return key, nil
}
