package recyclebinmutationv1

import (
	"context"
	"encoding/json"
	"strings"
	"unicode/utf8"

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

func ValidateRequest(ctx context.Context, request Request) 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 validateRequestShape(request)
}

func ValidateResponse(request Request, response Response) error {
	if err := validateRequestShape(request); err != nil {
		return internal()
	}
	if !common.ValidReceipts(response.Receipts, CapabilityName) {
		return incompatible()
	}
	if request.MinCatalogRevision != nil && response.Receipts.CatalogRevision < *request.MinCatalogRevision {
		return internal()
	}
	return nil
}

func validateRequestShape(request Request) error {
	if len(request.RecordID) > MaxRecordIdentifierBytes {
		return &Failure{Category: FailureResourceExhausted, LimitKind: LimitRecordID, Maximum: MaxRecordIdentifierBytes}
	}
	if !validRecordID(request.RecordID) {
		return invalid(InvalidComponentRecordID)
	}
	if request.MinCatalogRevision != nil && *request.MinCatalogRevision == 0 {
		return invalid(InvalidComponentOperation)
	}
	encoded, err := json.Marshal(request)
	if err != nil {
		return invalid(InvalidComponentResponse)
	}
	if len(encoded) > MaxSemanticRequestBytes {
		return &Failure{Category: FailureResourceExhausted, LimitKind: LimitSemanticRequest, Maximum: MaxSemanticRequestBytes}
	}
	return nil
}

func validRecordID(value string) bool {
	return strings.TrimSpace(value) != "" && len(value) <= MaxRecordIdentifierBytes && utf8.ValidString(value)
}

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

func incompatible() *Failure {
	return &Failure{
		Category: FailureIncompatible, RequiredCapability: CapabilityName,
		SupportedMajor: ProtocolMajor, SupportedMinorMin: ProtocolMinor,
		SupportedMinorMax: ProtocolMinor,
	}
}

func internal() *Failure { return &Failure{Category: FailureInternal} }
