package mapping

import (
	"errors"

	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/federatedloginv1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

var federatedLoginCapability = platformCapability(semantic.CapabilityName)

// ToFederatedLoginRequestStatusError projects only request decode and
// validation failures. It must be used before an owner operation starts, so a
// malformed request remains distinguishable from a valid request whose state
// is rejected by Consume.
func ToFederatedLoginRequestStatusError(err error) error {
	if shapeErr := requirePlatformSemanticShape(semantic.CapabilityName); shapeErr != nil {
		err = shapeErr
	}
	var failure *commonsemantic.Failure
	if errors.As(err, &failure) && !validFederatedLoginRequestFailure(failure, false) {
		err = &commonsemantic.Failure{Category: commonsemantic.FailureInternal}
	}
	return toStatusError(err, federatedLoginCapability)
}

// FromFederatedLoginRequestStatusError decodes only the finite request-failure
// grammar used before Begin or Consume reaches its owner operation.
func FromFederatedLoginRequestStatusError(err error) error {
	if shapeErr := requirePlatformSemanticShape(semantic.CapabilityName); shapeErr != nil {
		return shapeErr
	}
	decoded := fromStatusError(err, federatedLoginCapability)
	var failure *commonsemantic.Failure
	if errors.As(decoded, &failure) && !validFederatedLoginRequestFailure(failure, true) {
		return &commonsemantic.Failure{Category: commonsemantic.FailureInternal}
	}
	return decoded
}

// ToFederatedLoginBeginStatusError projects an owner result only after the
// Begin request has passed semantic validation.
func ToFederatedLoginBeginStatusError(err error) error {
	if shapeErr := requirePlatformSemanticShape(semantic.CapabilityName); shapeErr != nil {
		err = shapeErr
	}
	return toFederatedLoginOperationStatusError(err, semantic.NormalizeBeginOperationFailure)
}

// FromFederatedLoginBeginStatusError decodes and re-enforces the Begin owner
// failure grammar at the client boundary.
func FromFederatedLoginBeginStatusError(err error) error {
	if shapeErr := requirePlatformSemanticShape(semantic.CapabilityName); shapeErr != nil {
		return shapeErr
	}
	return semantic.NormalizeBeginOperationFailure(fromStatusError(err, federatedLoginCapability))
}

// ToFederatedLoginConsumeStatusError projects an owner result only after the
// Consume request has passed semantic validation. State-shaped outcomes are
// normalized before any gRPC code or RuntimeErrorDetail is selected.
func ToFederatedLoginConsumeStatusError(err error) error {
	if shapeErr := requirePlatformSemanticShape(semantic.CapabilityName); shapeErr != nil {
		err = shapeErr
	}
	return toFederatedLoginOperationStatusError(err, semantic.NormalizeConsumeOperationFailure)
}

// FromFederatedLoginConsumeStatusError decodes and re-enforces the Consume
// owner failure grammar. Legacy undetailed state-shaped codes also collapse to
// UNAUTHENTICATED instead of becoming a client-visible validity oracle.
func FromFederatedLoginConsumeStatusError(err error) error {
	if shapeErr := requirePlatformSemanticShape(semantic.CapabilityName); shapeErr != nil {
		return shapeErr
	}
	decoded := fromStatusError(err, federatedLoginCapability)
	var failure *commonsemantic.Failure
	if errors.As(decoded, &failure) && failure.Category == commonsemantic.FailureInternal {
		switch status.Code(err) {
		case codes.InvalidArgument, codes.PermissionDenied, codes.NotFound:
			decoded = &semantic.Failure{Category: semantic.FailureUnauthenticated}
		}
	}
	return semantic.NormalizeConsumeOperationFailure(decoded)
}

func toFederatedLoginOperationStatusError(err error, normalize func(error) error) error {
	normalized := normalize(err)
	var failure *commonsemantic.Failure
	if errors.As(normalized, &failure) && failure.Category == semantic.FailureResourceExhausted && failure.LimitKind == "" {
		// The trusted host may reject work before it can attach a semantic
		// admission detail. Preserve the same bare transport status accepted by
		// fromStatusError; emitting an empty LimitViolation detail would be an
		// invalid semantic shape.
		return status.Error(codes.ResourceExhausted, "runtime request failed")
	}
	return toStatusError(normalized, federatedLoginCapability)
}

func validFederatedLoginRequestFailure(failure *commonsemantic.Failure, decoded bool) bool {
	if failure == nil || failure.Receipts != nil {
		return false
	}
	switch failure.Category {
	case semantic.FailureInvalidRequest:
		if failure.CanonicalField != "" {
			return false
		}
		switch failure.InvalidComponent {
		case semantic.InvalidComponentAppInstallationID, semantic.InvalidComponentFieldValue,
			semantic.InvalidComponentDeadline, semantic.InvalidComponentResponse:
			return true
		default:
			return false
		}
	case semantic.FailureResourceExhausted:
		return (decoded && failure.LimitKind == "") ||
			(failure.LimitKind == semantic.LimitAppInstallationID && failure.Maximum == semantic.MaxIdentifierBytes)
	case semantic.FailureIncompatible:
		if !decoded {
			return true
		}
		return failure.RequiredCapability == semantic.CapabilityName &&
			failure.SupportedMajor == semantic.ProtocolMajor &&
			failure.SupportedMinorMin <= semantic.ProtocolMinor &&
			failure.SupportedMinorMax >= semantic.ProtocolMinor
	case semantic.FailureCanceled, semantic.FailureDeadlineExceeded, semantic.FailureInternal:
		return true
	default:
		return false
	}
}
