package commandv1

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"

	common "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
)

var identity = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_.:/-]*$`)

func ValidateRequest(ctx context.Context, request Request) error {
	if err := validateContext(ctx); err != nil {
		return err
	}
	if !validOpaque(request.CommandID) || !validOpaque(request.IdempotencyKey) ||
		!ValidCommandKind(request.CommandKind) ||
		(request.PartitionKey != "" && !validOpaque(request.PartitionKey)) ||
		request.PayloadFormatVersion == 0 ||
		len(request.Payload) == 0 || len(request.Payload) > MaxPayloadBytes ||
		request.PayloadDigest != ComputePayloadDigest(request.Payload) {
		return invalid(common.InvalidComponentCommandData)
	}
	return validateSize(request, MaxSemanticRequestBytes, common.LimitSemanticRequest)
}

// ValidCommandKind is the one neutral identifier grammar shared by command
// registration, authorized consumer bindings, and command acceptance.
func ValidCommandKind(value string) bool {
	return validIdentity(value)
}

func ValidateResponse(request Request, response Response) error {
	if !common.ValidReceipts(response.Receipts, CapabilityName) ||
		response.CommandID != request.CommandID || !validOpaque(response.CommandID) {
		return internal()
	}
	return validateSize(response, MaxSemanticResponseBytes, common.LimitSemanticResponse)
}

func ComputePayloadDigest(value []byte) string {
	sum := sha256.Sum256(value)
	return "sha256:" + hex.EncodeToString(sum[:])
}

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

func validIdentity(value string) bool {
	return len(value) <= MaxIdentifierBytes && identity.MatchString(value)
}

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

func validateSize(value any, maximum int, kind common.LimitKind) error {
	encoded, err := json.Marshal(value)
	if err != nil {
		return internal()
	}
	if len(encoded) > maximum {
		return &Failure{Category: common.FailureResourceExhausted, LimitKind: kind, Maximum: uint64(maximum)}
	}
	return nil
}

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